Skip to content

Fix inline assembly #12798

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 1 commit into from
Mar 14, 2014
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
9 changes: 5 additions & 4 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,14 +1259,15 @@ impl Liveness {
}

ExprInlineAsm(ref ia) => {
let succ = ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
self.propagate_through_expr(expr, succ)
});
ia.outputs.rev_iter().fold(succ, |succ, &(_, expr)| {
let succ = ia.outputs.rev_iter().fold(succ, |succ, &(_, 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)
});
// Inputs are executed first. Propagate last because of rev order
ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
self.propagate_through_expr(expr, succ)
})
}

Expand Down
119 changes: 69 additions & 50 deletions src/libsyntax/ext/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,25 @@ enum State {
Outputs,
Inputs,
Clobbers,
Options
Options,
StateNone
}

fn next_state(s: State) -> Option<State> {
match s {
Asm => Some(Outputs),
Outputs => Some(Inputs),
Inputs => Some(Clobbers),
Clobbers => Some(Options),
Options => None
impl State {
fn next(&self) -> State {
match *self {
Asm => Outputs,
Outputs => Inputs,
Inputs => Clobbers,
Clobbers => Options,
Options => StateNone,
StateNone => StateNone
}
}
}

static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];

pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
-> base::MacResult {
let mut p = parse::new_parser_from_tts(cx.parse_sess(),
Expand All @@ -59,9 +65,9 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])

let mut state = Asm;

// Not using labeled break to get us through one round of bootstrapping.
let mut continue_ = true;
while continue_ {
let mut read_write_operands = Vec::new();

'statement: loop {
match state {
Asm => {
let (s, style) = match expr_to_str(cx, p.parse_expr(),
Expand All @@ -84,18 +90,33 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])

let (constraint, _str_style) = p.parse_str();

if constraint.get().starts_with("+") {
cx.span_unimpl(p.last_span,
"'+' (read+write) output operand constraint modifier");
} else if !constraint.get().starts_with("=") {
cx.span_err(p.last_span, "output operand constraint lacks '='");
}
let span = p.last_span;

p.expect(&token::LPAREN);
let out = p.parse_expr();
p.expect(&token::RPAREN);

outputs.push((constraint, out));
// Expands a read+write operand into two operands.
//
// Use '+' modifier when you want the same expression
// to be both an input and an output at the same time.
// It's the opposite of '=&' which means that the memory
// cannot be shared with any other operand (usually when
// a register is clobbered early.)
let output = match constraint.get().slice_shift_char() {
(Some('='), _) => None,
(Some('+'), operand) => {
// Save a reference to the output
read_write_operands.push((outputs.len(), out));
Some(token::intern_and_get_ident("=" + operand))
}
_ => {
cx.span_err(span, "output operand constraint lacks '=' or '+'");
None
}
};

outputs.push((output.unwrap_or(constraint), out));
}
}
Inputs => {
Expand Down Expand Up @@ -135,6 +156,10 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
let (s, _str_style) = p.parse_str();
let clob = format!("~\\{{}\\}", s);
clobs.push(clob);

if OPTIONS.iter().any(|opt| s.equiv(opt)) {
cx.span_warn(p.last_span, "expected a clobber, but found an option");
}
}

cons = clobs.connect(",");
Expand All @@ -143,56 +168,50 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
let (option, _str_style) = p.parse_str();

if option.equiv(&("volatile")) {
// Indicates that the inline assembly has side effects
// and must not be optimized out along with its outputs.
volatile = true;
} else if option.equiv(&("alignstack")) {
alignstack = true;
} else if option.equiv(&("intel")) {
dialect = ast::AsmIntel;
} else {
cx.span_warn(p.last_span, "unrecognized option");
}

if p.token == token::COMMA {
p.eat(&token::COMMA);
}
}
StateNone => ()
}

while p.token == token::COLON ||
p.token == token::MOD_SEP ||
p.token == token::EOF {
state = if p.token == token::COLON {
p.bump();
match next_state(state) {
Some(x) => x,
None => {
continue_ = false;
break
}
loop {
// MOD_SEP is a double colon '::' without space in between.
// When encountered, the state must be advanced twice.
match (&p.token, state.next(), state.next().next()) {
(&token::COLON, StateNone, _) |
(&token::MOD_SEP, _, StateNone) => {
p.bump();
break 'statement;
}
} else if p.token == token::MOD_SEP {
p.bump();
let s = match next_state(state) {
Some(x) => x,
None => {
continue_ = false;
break
}
};
match next_state(s) {
Some(x) => x,
None => {
continue_ = false;
break
}
(&token::COLON, st, _) |
(&token::MOD_SEP, _, st) => {
p.bump();
state = st;
}
} else if p.token == token::EOF {
continue_ = false;
break;
} else {
state
};
(&token::EOF, _, _) => break 'statement,
_ => break
}
}
}

// Append an input operand, with the form of ("0", expr)
// that links to an output operand.
for &(i, out) in read_write_operands.iter() {
inputs.push((token::intern_and_get_ident(i.to_str()), out));
}

MRExpr(@ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprInlineAsm(ast::InlineAsm {
Expand Down
41 changes: 41 additions & 0 deletions src/test/compile-fail/asm-misplaced-option.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2012-2014 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.

// ignore-fast #[feature] doesn't work with check-fast
#[feature(asm)];

#[allow(dead_code)];

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
// assignment not dead
let mut x: int = 0;
unsafe {
// extra colon
asm!("mov $1, $0" : "=r"(x) : "r"(5u), "0"(x) : : "cc");
//~^ WARNING unrecognized option
}
assert_eq!(x, 5);

unsafe {
// comma in place of a colon
asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile");
//~^ WARNING expected a clobber, but found an option
}
assert_eq!(x, 13);
}

// #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
// pub fn main() {}

// At least one error is needed so that compilation fails
#[static_assert]
static b: bool = false; //~ ERROR static assertion failed
67 changes: 67 additions & 0 deletions src/test/run-pass/asm-in-out-operand.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2012-2014 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.

// ignore-fast #[feature] doesn't work with check-fast
#[feature(asm)];

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
unsafe fn next_power_of_2(n: u32) -> u32 {
let mut tmp = n;
asm!("dec $0" : "+rm"(tmp) :: "cc");
let mut shift = 1u;
while shift <= 16 {
asm!(
"shr %cl, $2
or $2, $0
shl $$1, $1"
: "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc"
);
}
asm!("inc $0" : "+rm"(tmp) :: "cc");
return tmp;
}

#[cfg(target_arch = "x86")]
#[cfg(target_arch = "x86_64")]
pub fn main() {
unsafe {
assert_eq!(64, next_power_of_2(37));
assert_eq!(2147483648, next_power_of_2(2147483647));
}

let mut y: int = 5;
let x: int;
unsafe {
// Treat the output as initialization.
asm!(
"shl $2, $1
add $3, $1
mov $1, $0"
: "=r"(x), "+r"(y) : "i"(3u), "ir"(7u) : "cc"
);
}
assert_eq!(x, 47);
assert_eq!(y, 47);

let mut x = x + 1;
assert_eq!(x, 48);

unsafe {
// Assignment to mutable.
// Early clobber "&":
// Forbids the use of a single register by both operands.
asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc");
}
assert_eq!(x, 60);
}

#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
pub fn main() {}