diff options
| author | Piotr Czarnecki <pioczarn@gmail.com> | 2014-03-09 23:41:18 +0100 |
|---|---|---|
| committer | Piotr Czarnecki <pioczarn@gmail.com> | 2014-03-13 22:38:15 +0100 |
| commit | 2a1bd2ff9f9039df4b3c0158eac3589a222a2833 (patch) | |
| tree | e69d5539a60fe03034586b75ca55f9825cf520fc /src/libsyntax | |
| parent | 3316a0e6b2ad9352bab58e7c046ef3d212411d82 (diff) | |
| download | rust-2a1bd2ff9f9039df4b3c0158eac3589a222a2833.tar.gz rust-2a1bd2ff9f9039df4b3c0158eac3589a222a2833.zip | |
Fix and improve inline assembly.
Read+write modifier Some documentation in asm.rs rpass and cfail 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 { |
