diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 5b931857dec..de540e5cfb3 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -368,7 +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; + let &(_, ref expr, _, _) = a; &**expr }), post_inputs); self.add_ast_node(expr.id, &[post_outputs]) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index ce8d74bf191..316b49e1de4 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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, + for &(_, ref output, is_rw, is_indirect) in &ia.outputs { + if is_indirect { + self.consume_expr(&**output); + } else { + self.mutate_expr(expr, &**output, if is_rw { WriteAndRead } else { JustWrite }); + } } } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2a8b1b83d22..5170b377324 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -1166,12 +1166,18 @@ 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, &(_, ref expr, _, is_indirect)| { + // see comment on lvalues + // in propagate_through_lvalue_components() + if is_indirect { + self.propagate_through_expr(&**expr, succ) + } else { + let succ = self.write_lvalue(&**expr, succ, ACC_WRITE); + self.propagate_through_lvalue_components(&**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,8 +1422,10 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { } // Output operands must be lvalues - for &(_, ref out, _) in &ia.outputs { - this.check_lvalue(&**out); + for &(_, ref out, _, is_indirect) in &ia.outputs { + if !is_indirect { + this.check_lvalue(&**out); + } this.visit_expr(&**out); } diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index 5f39376d156..c9a09989f04 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -1127,7 +1127,9 @@ pub fn noop_fold_expr(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(|(c, out, is_rw, is_indirect)| { + (c, folder.fold_expr(out), is_rw, is_indirect) + }), asm: asm, asm_str_style: asm_str_style, clobbers: clobbers, diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index beb7059d73c..bc95599fb7a 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -891,7 +891,7 @@ pub enum Ty_ { pub struct InlineAsm { pub asm: InternedString, pub asm_str_style: StrStyle, - pub outputs: Vec<(InternedString, P, bool)>, + pub outputs: Vec<(InternedString, P, bool, bool)>, pub inputs: Vec<(InternedString, P)>, pub clobbers: Vec, pub volatile: bool, diff --git a/src/librustc_front/intravisit.rs b/src/librustc_front/intravisit.rs index 3a43feb8ba7..d0f2a9dca17 100644 --- a/src/librustc_front/intravisit.rs +++ b/src/librustc_front/intravisit.rs @@ -803,7 +803,7 @@ 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 { + for &(_, ref output, _, _) in &ia.outputs { visitor.visit_expr(&output) } } diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index e8c4a6484e2..e5ea737f621 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -1202,8 +1202,8 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { .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(|&(ref c, ref out, is_rw, is_indirect)| { + (c.clone(), lower_expr(lctx, out), is_rw, is_indirect) }) .collect(), asm: asm.clone(), diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs index e059a4ed5f6..c8775000268 100644 --- a/src/librustc_front/print/pprust.rs +++ b/src/librustc_front/print/pprust.rs @@ -1502,7 +1502,7 @@ 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)| { + try!(self.commasep(Inconsistent, &a.outputs, |s, &(ref co, ref o, is_rw, _)| { match co.slice_shift_char() { Some(('=', operand)) if is_rw => { try!(s.print_string(&format!("+{}", operand), ast::CookedStr)) diff --git a/src/librustc_trans/trans/asm.rs b/src/librustc_trans/trans/asm.rs index c59237e3078..efa34fbcbdc 100644 --- a/src/librustc_trans/trans/asm.rs +++ b/src/librustc_trans/trans/asm.rs @@ -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))| { + let mut outputs = Vec::new(); + let mut inputs = Vec::new(); + for (i, &(ref c, ref out, is_rw, is_indirect)) in ia.outputs.iter().enumerate() { constraints.push((*c).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 { + if is_indirect { bcx = callee::trans_arg_datum(bcx, expr_ty(bcx, &**out), out_datum, cleanup::CustomScope(temp_scope), callee::DontAutorefArg, - &mut ext_inputs); - ext_constraints.push(i.to_string()); + &mut inputs); + if 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 is_rw { + bcx = callee::trans_arg_datum(bcx, + expr_ty(bcx, &**out), + out_datum, + cleanup::CustomScope(temp_scope), + callee::DontAutorefArg, + &mut ext_inputs); + ext_constraints.push(i.to_string()); + } } - val - - }).collect::>(); + } // Now the input operands - let mut inputs = Vec::new(); for &(ref c, ref input) in &ia.inputs { constraints.push((*c).clone()); diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs index 0c424de9e10..c8a66fb611d 100644 --- a/src/librustc_trans/trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs @@ -480,7 +480,7 @@ fn walk_expr(cx: &CrateContext, walk_expr(cx, &**exp, scope_stack, scope_map); } - for &(_, ref exp, _) in outputs { + for &(_, ref exp, _, _) in outputs { walk_expr(cx, &**exp, scope_stack, scope_map); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index ed8bb6a9625..722273ee0f8 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3393,7 +3393,7 @@ 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 { + for &(_, ref out, _, _) in &ia.outputs { check_expr(fcx, &**out); } fcx.write_nil(id); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index f11291fc0f7..ac3e26d0335 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1462,7 +1462,7 @@ pub enum AsmDialect { pub struct InlineAsm { pub asm: InternedString, pub asm_str_style: StrStyle, - pub outputs: Vec<(InternedString, P, bool)>, + pub outputs: Vec<(InternedString, P, bool, bool)>, pub inputs: Vec<(InternedString, P)>, pub clobbers: Vec, pub volatile: bool, diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index d968858f634..f643f0be276 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -125,7 +125,8 @@ 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((output.unwrap_or(constraint), out, is_rw, is_indirect)); } } Inputs => { @@ -139,9 +140,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 '+'"); } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 73f2c51b246..8619c3c0bdb 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1303,8 +1303,8 @@ pub fn noop_fold_expr(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(|(c, out, is_rw, is_indirect)| { + (c, folder.fold_expr(out), is_rw, is_indirect) }), asm: asm, asm_str_style: asm_str_style, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index aa55cb847fa..a0b7df3e2e9 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2221,7 +2221,7 @@ impl<'a> State<'a> { try!(self.word_space(":")); try!(self.commasep(Inconsistent, &a.outputs, - |s, &(ref co, ref o, is_rw)| { + |s, &(ref co, ref o, is_rw, _)| { match co.slice_shift_char() { Some(('=', operand)) if is_rw => { try!(s.print_string(&format!("+{}", operand), diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index cdc11fb2c1c..5ede6596e8f 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -786,7 +786,7 @@ 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 { + for &(_, ref output, _, _) in &ia.outputs { visitor.visit_expr(&output) } } diff --git a/src/test/run-pass/asm-indirect-memory.rs b/src/test/run-pass/asm-indirect-memory.rs index 80fd548dfe3..d1873afb3a9 100644 --- a/src/test/run-pass/asm-indirect-memory.rs +++ b/src/test/run-pass/asm-indirect-memory.rs @@ -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")))]