diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 02a947a0ddc..3be28bad5ab 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -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) }) } diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index 6080613460d..8c1e27d8b01 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -27,19 +27,25 @@ enum State { Outputs, Inputs, Clobbers, - Options + Options, + StateNone } -fn next_state(s: State) -> Option { - 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(), @@ -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(), @@ -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 => { @@ -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(","); @@ -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 { diff --git a/src/test/compile-fail/asm-misplaced-option.rs b/src/test/compile-fail/asm-misplaced-option.rs new file mode 100644 index 00000000000..595247143ed --- /dev/null +++ b/src/test/compile-fail/asm-misplaced-option.rs @@ -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 or the MIT license +// , 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 diff --git a/src/test/run-pass/asm-in-out-operand.rs b/src/test/run-pass/asm-in-out-operand.rs new file mode 100644 index 00000000000..11d4321b3e9 --- /dev/null +++ b/src/test/run-pass/asm-in-out-operand.rs @@ -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 or the MIT license +// , 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() {}