]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/asm.rs
auto merge of #21132 : sfackler/rust/wait_timeout, r=alexcrichton
[rust.git] / src / libsyntax / ext / asm.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /*
12  * Inline assembly support.
13  */
14 use self::State::*;
15
16 use ast;
17 use codemap;
18 use codemap::Span;
19 use ext::base;
20 use ext::base::*;
21 use parse::token::InternedString;
22 use parse::token;
23 use ptr::P;
24
25 enum State {
26     Asm,
27     Outputs,
28     Inputs,
29     Clobbers,
30     Options,
31     StateNone
32 }
33
34 impl State {
35     fn next(&self) -> State {
36         match *self {
37             Asm       => Outputs,
38             Outputs   => Inputs,
39             Inputs    => Clobbers,
40             Clobbers  => Options,
41             Options   => StateNone,
42             StateNone => StateNone
43         }
44     }
45 }
46
47 static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
48
49 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
50                        -> Box<base::MacResult+'cx> {
51     let mut p = cx.new_parser_from_tts(tts);
52     let mut asm = InternedString::new("");
53     let mut asm_str_style = None;
54     let mut outputs = Vec::new();
55     let mut inputs = Vec::new();
56     let mut clobs = Vec::new();
57     let mut volatile = false;
58     let mut alignstack = false;
59     let mut dialect = ast::AsmAtt;
60
61     let mut state = Asm;
62
63     'statement: loop {
64         match state {
65             Asm => {
66                 if asm_str_style.is_some() {
67                     // If we already have a string with instructions,
68                     // ending up in Asm state again is an error.
69                     cx.span_err(sp, "malformed inline assembly");
70                     return DummyResult::expr(sp);
71                 }
72                 let (s, style) = match expr_to_string(cx, p.parse_expr(),
73                                                    "inline assembly must be a string literal") {
74                     Some((s, st)) => (s, st),
75                     // let compilation continue
76                     None => return DummyResult::expr(sp),
77                 };
78                 asm = s;
79                 asm_str_style = Some(style);
80             }
81             Outputs => {
82                 while p.token != token::Eof &&
83                       p.token != token::Colon &&
84                       p.token != token::ModSep {
85
86                     if outputs.len() != 0 {
87                         p.eat(&token::Comma);
88                     }
89
90                     let (constraint, _str_style) = p.parse_str();
91
92                     let span = p.last_span;
93
94                     p.expect(&token::OpenDelim(token::Paren));
95                     let out = p.parse_expr();
96                     p.expect(&token::CloseDelim(token::Paren));
97
98                     // Expands a read+write operand into two operands.
99                     //
100                     // Use '+' modifier when you want the same expression
101                     // to be both an input and an output at the same time.
102                     // It's the opposite of '=&' which means that the memory
103                     // cannot be shared with any other operand (usually when
104                     // a register is clobbered early.)
105                     let output = match constraint.get().slice_shift_char() {
106                         Some(('=', _)) => None,
107                         Some(('+', operand)) => {
108                             Some(token::intern_and_get_ident(&format!(
109                                         "={}", operand)[]))
110                         }
111                         _ => {
112                             cx.span_err(span, "output operand constraint lacks '=' or '+'");
113                             None
114                         }
115                     };
116
117                     let is_rw = output.is_some();
118                     outputs.push((output.unwrap_or(constraint), out, is_rw));
119                 }
120             }
121             Inputs => {
122                 while p.token != token::Eof &&
123                       p.token != token::Colon &&
124                       p.token != token::ModSep {
125
126                     if inputs.len() != 0 {
127                         p.eat(&token::Comma);
128                     }
129
130                     let (constraint, _str_style) = p.parse_str();
131
132                     if constraint.get().starts_with("=") {
133                         cx.span_err(p.last_span, "input operand constraint contains '='");
134                     } else if constraint.get().starts_with("+") {
135                         cx.span_err(p.last_span, "input operand constraint contains '+'");
136                     }
137
138                     p.expect(&token::OpenDelim(token::Paren));
139                     let input = p.parse_expr();
140                     p.expect(&token::CloseDelim(token::Paren));
141
142                     inputs.push((constraint, input));
143                 }
144             }
145             Clobbers => {
146                 while p.token != token::Eof &&
147                       p.token != token::Colon &&
148                       p.token != token::ModSep {
149
150                     if clobs.len() != 0 {
151                         p.eat(&token::Comma);
152                     }
153
154                     let (s, _str_style) = p.parse_str();
155
156                     if OPTIONS.iter().any(|&opt| s == opt) {
157                         cx.span_warn(p.last_span, "expected a clobber, found an option");
158                     }
159                     clobs.push(s);
160                 }
161             }
162             Options => {
163                 let (option, _str_style) = p.parse_str();
164
165                 if option == "volatile" {
166                     // Indicates that the inline assembly has side effects
167                     // and must not be optimized out along with its outputs.
168                     volatile = true;
169                 } else if option == "alignstack" {
170                     alignstack = true;
171                 } else if option == "intel" {
172                     dialect = ast::AsmIntel;
173                 } else {
174                     cx.span_warn(p.last_span, "unrecognized option");
175                 }
176
177                 if p.token == token::Comma {
178                     p.eat(&token::Comma);
179                 }
180             }
181             StateNone => ()
182         }
183
184         loop {
185             // MOD_SEP is a double colon '::' without space in between.
186             // When encountered, the state must be advanced twice.
187             match (&p.token, state.next(), state.next().next()) {
188                 (&token::Colon, StateNone, _)   |
189                 (&token::ModSep, _, StateNone) => {
190                     p.bump();
191                     break 'statement;
192                 }
193                 (&token::Colon, st, _)   |
194                 (&token::ModSep, _, st) => {
195                     p.bump();
196                     state = st;
197                 }
198                 (&token::Eof, _, _) => break 'statement,
199                 _ => break
200             }
201         }
202     }
203
204     let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
205         call_site: sp,
206         callee: codemap::NameAndSpan {
207             name: "asm".to_string(),
208             format: codemap::MacroBang,
209             span: None,
210         },
211     });
212
213     MacExpr::new(P(ast::Expr {
214         id: ast::DUMMY_NODE_ID,
215         node: ast::ExprInlineAsm(ast::InlineAsm {
216             asm: token::intern_and_get_ident(asm.get()),
217             asm_str_style: asm_str_style.unwrap(),
218             outputs: outputs,
219             inputs: inputs,
220             clobbers: clobs,
221             volatile: volatile,
222             alignstack: alignstack,
223             dialect: dialect,
224             expn_id: expn_id,
225         }),
226         span: sp
227     }))
228 }