Skip to content

Properly support inline asm indirect output operands #29735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
Expand Down
10 changes: 7 additions & 3 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 });
}
}
}

Expand Down
27 changes: 18 additions & 9 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion src/librustc_front/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,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,
Expand Down
10 changes: 9 additions & 1 deletion src/librustc_front/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,11 +887,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,
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_front/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/librustc_front/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,8 +1202,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(),
Expand Down
10 changes: 5 additions & 5 deletions src/librustc_front/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}));
Expand Down
38 changes: 25 additions & 13 deletions src/librustc_trans/trans/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_trans/trans/debuginfo/create_scope_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3393,8 +3393,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);
}
Expand Down
10 changes: 9 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 9 additions & 3 deletions src/libsyntax/ext/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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 '+'");
}

Expand Down
9 changes: 7 additions & 2 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 5 additions & 5 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2221,16 +2221,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(())
}));
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
}
Expand Down
16 changes: 14 additions & 2 deletions src/test/run-pass/asm-indirect-memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")))]
Expand Down