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