auto merge of #9850 : eddyb/rust/inline-asm-constraints, r=luqmana

I've implemented analysis support for the [GCC '=' write-only inline asm constraint modifier](http://gcc.gnu.org/onlinedocs/gcc/Modifiers.html). I had more changes, for '+' (read+write) as well, but it turns out LLVM doesn't support '+' at all.

I've removed the need for wrapping each output in ExprAddrOf, as that would require unwrapping almost everywhere and it was harder to reason about in borrowck than ExprAssign's LHS.

With this change, rustc will treat (in respect to validity of accessing a local) code like this:
```rust
let x: int;
unsafe {
    asm!("mov $1, $0" : "=r"(x) : "r"(5u));
}
```
as if it were this:
```rust
let x : int;
x = 5;
```

Previously, the local was required to be both mutable and initialized, and the write effect wasn't recorded.
This commit is contained in:
bors 2013-10-17 23:16:22 -07:00
commit d0d554456a
10 changed files with 193 additions and 50 deletions

View File

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -839,6 +839,11 @@ fn check_loans_in_expr<'a>(this: &mut CheckLoanCtxt<'a>,
expr.span, expr.span,
[]); []);
} }
ast::ExprInlineAsm(ref ia) => {
for &(_, out) in ia.outputs.iter() {
this.check_assignment(out);
}
}
_ => { } _ => { }
} }
} }

View File

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -309,6 +309,23 @@ fn gather_loans_in_expr(this: &mut GatherLoanCtxt,
visit::walk_expr(this, ex, ()); visit::walk_expr(this, ex, ());
} }
ast::ExprInlineAsm(ref ia) => {
for &(_, out) in ia.outputs.iter() {
let out_cmt = this.bccx.cat_expr(out);
match opt_loan_path(out_cmt) {
Some(out_lp) => {
gather_moves::gather_assignment(this.bccx, this.move_data,
ex.id, ex.span,
out_lp, out.id);
}
None => {
// See the comment for ExprAssign.
}
}
}
visit::walk_expr(this, ex, ());
}
_ => { _ => {
visit::walk_expr(this, ex, ()); visit::walk_expr(this, ex, ());
} }

View File

@ -1,4 +1,4 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -1227,12 +1227,15 @@ pub fn propagate_through_expr(&self, expr: @Expr, succ: LiveNode)
self.propagate_through_expr(e, succ) self.propagate_through_expr(e, succ)
} }
ExprInlineAsm(ref ia) =>{ ExprInlineAsm(ref ia) => {
let succ = do ia.inputs.rev_iter().fold(succ) |succ, &(_, expr)| { let succ = do ia.inputs.rev_iter().fold(succ) |succ, &(_, expr)| {
self.propagate_through_expr(expr, succ) self.propagate_through_expr(expr, succ)
}; };
do ia.outputs.rev_iter().fold(succ) |succ, &(_, expr)| { do ia.outputs.rev_iter().fold(succ) |succ, &(_, expr)| {
self.propagate_through_expr(expr, succ) // 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)
} }
} }
@ -1478,12 +1481,7 @@ fn check_expr(this: &mut Liveness, expr: @Expr) {
// Output operands must be lvalues // Output operands must be lvalues
for &(_, out) in ia.outputs.iter() { for &(_, out) in ia.outputs.iter() {
match out.node { this.check_lvalue(out);
ExprAddrOf(_, inner) => {
this.check_lvalue(inner);
}
_ => {}
}
this.visit_expr(out, ()); this.visit_expr(out, ());
} }

View File

@ -18,6 +18,8 @@
use middle::trans::build::*; use middle::trans::build::*;
use middle::trans::callee; use middle::trans::callee;
use middle::trans::common::*; use middle::trans::common::*;
use middle::trans::expr::*;
use middle::trans::type_of::*;
use middle::ty; use middle::ty;
use middle::trans::type_::Type; use middle::trans::type_::Type;
@ -30,34 +32,15 @@ pub fn trans_inline_asm(bcx: @mut Block, ia: &ast::inline_asm) -> @mut Block {
let mut bcx = bcx; let mut bcx = bcx;
let mut constraints = ~[]; let mut constraints = ~[];
let mut cleanups = ~[]; let mut cleanups = ~[];
let mut aoutputs = ~[]; let mut output_types = ~[];
// Prepare the output operands // Prepare the output operands
let outputs = do ia.outputs.map |&(c, out)| { let outputs = do ia.outputs.map |&(c, out)| {
constraints.push(c); constraints.push(c);
aoutputs.push(unpack_result!(bcx, { let out_datum = unpack_datum!(bcx, trans_to_datum(bcx, out));
callee::trans_arg_expr(bcx, output_types.push(type_of(bcx.ccx(), out_datum.ty));
expr_ty(bcx, out), out_datum.val
ty::ByCopy,
out,
&mut cleanups,
callee::DontAutorefArg)
}));
let e = match out.node {
ast::ExprAddrOf(_, e) => e,
_ => fail2!("Expression must be addr of")
};
unpack_result!(bcx, {
callee::trans_arg_expr(bcx,
expr_ty(bcx, e),
ty::ByCopy,
e,
&mut cleanups,
callee::DontAutorefArg)
})
}; };
@ -92,7 +75,7 @@ pub fn trans_inline_asm(bcx: @mut Block, ia: &ast::inline_asm) -> @mut Block {
clobbers = format!("{},{}", ia.clobbers, clobbers); clobbers = format!("{},{}", ia.clobbers, clobbers);
} else { } else {
clobbers.push_str(ia.clobbers); clobbers.push_str(ia.clobbers);
}; }
// Add the clobbers to our constraints list // Add the clobbers to our constraints list
if clobbers.len() != 0 && constraints.len() != 0 { if clobbers.len() != 0 && constraints.len() != 0 {
@ -107,12 +90,12 @@ pub fn trans_inline_asm(bcx: @mut Block, ia: &ast::inline_asm) -> @mut Block {
let numOutputs = outputs.len(); let numOutputs = outputs.len();
// Depending on how many outputs we have, the return type is different // Depending on how many outputs we have, the return type is different
let output = if numOutputs == 0 { let output_type = if numOutputs == 0 {
Type::void() Type::void()
} else if numOutputs == 1 { } else if numOutputs == 1 {
val_ty(outputs[0]) output_types[0]
} else { } else {
Type::struct_(outputs.map(|o| val_ty(*o)), false) Type::struct_(output_types, false)
}; };
let dialect = match ia.dialect { let dialect = match ia.dialect {
@ -122,19 +105,17 @@ pub fn trans_inline_asm(bcx: @mut Block, ia: &ast::inline_asm) -> @mut Block {
let r = do ia.asm.with_c_str |a| { let r = do ia.asm.with_c_str |a| {
do constraints.with_c_str |c| { do constraints.with_c_str |c| {
InlineAsmCall(bcx, a, c, inputs, output, ia.volatile, ia.alignstack, dialect) InlineAsmCall(bcx, a, c, inputs, output_type, ia.volatile, ia.alignstack, dialect)
} }
}; };
// Again, based on how many outputs we have // Again, based on how many outputs we have
if numOutputs == 1 { if numOutputs == 1 {
let op = PointerCast(bcx, aoutputs[0], val_ty(outputs[0]).ptr_to()); Store(bcx, r, outputs[0]);
Store(bcx, r, op);
} else { } else {
for (i, o) in aoutputs.iter().enumerate() { for (i, o) in outputs.iter().enumerate() {
let v = ExtractValue(bcx, r, i); let v = ExtractValue(bcx, r, i);
let op = PointerCast(bcx, *o, val_ty(outputs[i]).ptr_to()); Store(bcx, v, *o);
Store(bcx, v, op);
} }
} }

View File

@ -75,16 +75,18 @@ pub fn expand_asm(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree])
} }
let (constraint, _str_style) = p.parse_str(); let (constraint, _str_style) = p.parse_str();
if constraint.starts_with("+") {
cx.span_unimpl(*p.last_span,
"'+' (read+write) output operand constraint modifier");
} else if !constraint.starts_with("=") {
cx.span_err(*p.last_span, "output operand constraint lacks '='");
}
p.expect(&token::LPAREN); p.expect(&token::LPAREN);
let out = p.parse_expr(); let out = p.parse_expr();
p.expect(&token::RPAREN); p.expect(&token::RPAREN);
let out = @ast::Expr {
id: ast::DUMMY_NODE_ID,
span: out.span,
node: ast::ExprAddrOf(ast::MutMutable, out)
};
outputs.push((constraint, out)); outputs.push((constraint, out));
} }
} }
@ -98,6 +100,13 @@ pub fn expand_asm(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree])
} }
let (constraint, _str_style) = p.parse_str(); let (constraint, _str_style) = p.parse_str();
if constraint.starts_with("=") {
cx.span_err(*p.last_span, "input operand constraint contains '='");
} else if constraint.starts_with("+") {
cx.span_err(*p.last_span, "input operand constraint contains '+'");
}
p.expect(&token::LPAREN); p.expect(&token::LPAREN);
let input = p.parse_expr(); let input = p.parse_expr();
p.expect(&token::RPAREN); p.expect(&token::RPAREN);

View File

@ -0,0 +1,27 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo(x: int) { info2!("{}", x); }
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
let x: int;
let y: int;
unsafe {
asm!("mov $1, $0" : "=r"(x) : "=r"(5u)); //~ ERROR input operand constraint contains '='
asm!("mov $1, $0" : "=r"(y) : "+r"(5u)); //~ ERROR input operand constraint contains '+'
}
foo(x);
foo(y);
}
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}

View File

@ -0,0 +1,26 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo(x: int) { info2!("{}", x); }
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
let x: int;
x = 1; //~ NOTE prior assignment occurs here
foo(x);
unsafe {
asm!("mov $1, $0" : "=r"(x) : "r"(5u)); //~ ERROR re-assignment of immutable variable `x`
}
foo(x);
}
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}

View File

@ -0,0 +1,24 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo(x: int) { info2!("{}", x); }
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
let x: int;
unsafe {
asm!("mov $1, $0" : "r"(x) : "r"(5u)); //~ ERROR output operand constraint lacks '='
}
foo(x);
}
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}

View File

@ -0,0 +1,24 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn foo(x: int) { info2!("{}", x); }
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
let x: int;
unsafe {
asm!("mov $1, $0" : "=r"(x) : "r"(x)); //~ ERROR use of possibly uninitialized value: `x`
}
foo(x);
}
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}

View File

@ -0,0 +1,32 @@
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
let x: int;
unsafe {
// Treat the output as initialization.
asm!("mov $1, $0" : "=r"(x) : "r"(5u));
}
assert_eq!(x, 5);
let mut x = x + 1;
assert_eq!(x, 6);
unsafe {
// Assignment to mutable.
asm!("mov $1, $0" : "=r"(x) : "r"(x + 7));
}
assert_eq!(x, 13);
}
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}