diff options
| author | bors <bors@rust-lang.org> | 2014-03-13 18:41:35 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-03-13 18:41:35 -0700 |
| commit | 98fa0f89b1cd406594bedc5803a1b6db53990a15 (patch) | |
| tree | b36eb4e868e24d2b8880e88870cef74df9d2f3a9 /src/libsyntax | |
| parent | b4d324334cb48198c27d782002d75eba14a6abde (diff) | |
| parent | 2a1bd2ff9f9039df4b3c0158eac3589a222a2833 (diff) | |
| download | rust-98fa0f89b1cd406594bedc5803a1b6db53990a15.tar.gz rust-98fa0f89b1cd406594bedc5803a1b6db53990a15.zip | |
auto merge of #12798 : pczarn/rust/inline-asm, r=alexcrichton
## read+write modifier '+'
This small sugar was left out in the original implementation (#5359).
When an output operand with the '+' modifier is encountered, we store the index of that operand alongside the expression to create and append an input operand later. The following lines are equivalent:
```
asm!("" : "+m"(expr));
asm!("" : "=m"(expr) : "0"(expr));
```
## misplaced options and clobbers give a warning
It's really annoying when a small typo might change behavior without any warning.
```
asm!("mov $1, $0" : "=r"(x) : "r"(8u) : "cc" , "volatile");
//~^ WARNING expected a clobber, but found an option
```
## liveness
Fixed incorrect order of propagation.
Sometimes it caused spurious warnings in code: `warning: value assigned to `i` is never read, #[warn(dead_assignment)] on by default`
~~Note: Rebased on top of another PR. (uses other changes)~~
* [x] Implement read+write
* [x] Warn about misplaced options
* [x] Fix liveness (`dead_assignment` lint)
* [x] Add all tests
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/asm.rs | 119 |
1 files changed, 69 insertions, 50 deletions
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<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(), @@ -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 { |
