]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/asm.rs
7aa93dee11df98acd3c4dceca3c0beb15158a863
[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::Span;
17 use ext::base;
18 use ext::base::*;
19 use parse;
20 use parse::token::InternedString;
21 use parse::token;
22
23 use std::gc::GC;
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: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
50                   -> Box<base::MacResult> {
51     let mut p = parse::new_parser_from_tts(cx.parse_sess(),
52                                            cx.cfg(),
53                                            tts.iter()
54                                               .map(|x| (*x).clone())
55                                               .collect());
56
57     let mut asm = InternedString::new("");
58     let mut asm_str_style = None;
59     let mut outputs = Vec::new();
60     let mut inputs = Vec::new();
61     let mut cons = "".to_string();
62     let mut volatile = false;
63     let mut alignstack = false;
64     let mut dialect = ast::AsmAtt;
65
66     let mut state = Asm;
67
68     let mut read_write_operands = Vec::new();
69
70     'statement: loop {
71         match state {
72             Asm => {
73                 let (s, style) = match expr_to_str(cx, p.parse_expr(),
74                                                    "inline assembly must be a string literal.") {
75                     Some((s, st)) => (s, st),
76                     // let compilation continue
77                     None => return DummyResult::expr(sp),
78                 };
79                 asm = s;
80                 asm_str_style = Some(style);
81             }
82             Outputs => {
83                 while p.token != token::EOF &&
84                       p.token != token::COLON &&
85                       p.token != token::MOD_SEP {
86
87                     if outputs.len() != 0 {
88                         p.eat(&token::COMMA);
89                     }
90
91                     let (constraint, _str_style) = p.parse_str();
92
93                     let span = p.last_span;
94
95                     p.expect(&token::LPAREN);
96                     let out = p.parse_expr();
97                     p.expect(&token::RPAREN);
98
99                     // Expands a read+write operand into two operands.
100                     //
101                     // Use '+' modifier when you want the same expression
102                     // to be both an input and an output at the same time.
103                     // It's the opposite of '=&' which means that the memory
104                     // cannot be shared with any other operand (usually when
105                     // a register is clobbered early.)
106                     let output = match constraint.get().slice_shift_char() {
107                         (Some('='), _) => None,
108                         (Some('+'), operand) => {
109                             // Save a reference to the output
110                             read_write_operands.push((outputs.len(), out));
111                             Some(token::intern_and_get_ident(format!(
112                                         "={}",
113                                         operand).as_slice()))
114                         }
115                         _ => {
116                             cx.span_err(span, "output operand constraint lacks '=' or '+'");
117                             None
118                         }
119                     };
120
121                     outputs.push((output.unwrap_or(constraint), out));
122                 }
123             }
124             Inputs => {
125                 while p.token != token::EOF &&
126                       p.token != token::COLON &&
127                       p.token != token::MOD_SEP {
128
129                     if inputs.len() != 0 {
130                         p.eat(&token::COMMA);
131                     }
132
133                     let (constraint, _str_style) = p.parse_str();
134
135                     if constraint.get().starts_with("=") {
136                         cx.span_err(p.last_span, "input operand constraint contains '='");
137                     } else if constraint.get().starts_with("+") {
138                         cx.span_err(p.last_span, "input operand constraint contains '+'");
139                     }
140
141                     p.expect(&token::LPAREN);
142                     let input = p.parse_expr();
143                     p.expect(&token::RPAREN);
144
145                     inputs.push((constraint, input));
146                 }
147             }
148             #[cfg(stage0)]
149             Clobbers => {
150                 let mut clobs = Vec::new();
151                 while p.token != token::EOF &&
152                       p.token != token::COLON &&
153                       p.token != token::MOD_SEP {
154
155                     if clobs.len() != 0 {
156                         p.eat(&token::COMMA);
157                     }
158
159                     let (s, _str_style) = p.parse_str();
160                     let clob = format!("~\\{{}\\}", s);
161                     clobs.push(clob);
162
163                     if OPTIONS.iter().any(|opt| s.equiv(opt)) {
164                         cx.span_warn(p.last_span, "expected a clobber, but found an option");
165                     }
166                 }
167
168                 cons = clobs.connect(",");
169             }
170             #[cfg(not(stage0))]
171             Clobbers => {
172                 let mut clobs = Vec::new();
173                 while p.token != token::EOF &&
174                       p.token != token::COLON &&
175                       p.token != token::MOD_SEP {
176
177                     if clobs.len() != 0 {
178                         p.eat(&token::COMMA);
179                     }
180
181                     let (s, _str_style) = p.parse_str();
182                     let clob = format!("~{{{}}}", s);
183                     clobs.push(clob);
184
185                     if OPTIONS.iter().any(|opt| s.equiv(opt)) {
186                         cx.span_warn(p.last_span, "expected a clobber, but found an option");
187                     }
188                 }
189
190                 cons = clobs.connect(",");
191             }
192             Options => {
193                 let (option, _str_style) = p.parse_str();
194
195                 if option.equiv(&("volatile")) {
196                     // Indicates that the inline assembly has side effects
197                     // and must not be optimized out along with its outputs.
198                     volatile = true;
199                 } else if option.equiv(&("alignstack")) {
200                     alignstack = true;
201                 } else if option.equiv(&("intel")) {
202                     dialect = ast::AsmIntel;
203                 } else {
204                     cx.span_warn(p.last_span, "unrecognized option");
205                 }
206
207                 if p.token == token::COMMA {
208                     p.eat(&token::COMMA);
209                 }
210             }
211             StateNone => ()
212         }
213
214         loop {
215             // MOD_SEP is a double colon '::' without space in between.
216             // When encountered, the state must be advanced twice.
217             match (&p.token, state.next(), state.next().next()) {
218                 (&token::COLON, StateNone, _)   |
219                 (&token::MOD_SEP, _, StateNone) => {
220                     p.bump();
221                     break 'statement;
222                 }
223                 (&token::COLON, st, _)   |
224                 (&token::MOD_SEP, _, st) => {
225                     p.bump();
226                     state = st;
227                 }
228                 (&token::EOF, _, _) => break 'statement,
229                 _ => break
230             }
231         }
232     }
233
234     // Append an input operand, with the form of ("0", expr)
235     // that links to an output operand.
236     for &(i, out) in read_write_operands.iter() {
237         inputs.push((token::intern_and_get_ident(i.to_str().as_slice()),
238                                                  out));
239     }
240
241     MacExpr::new(box(GC) ast::Expr {
242         id: ast::DUMMY_NODE_ID,
243         node: ast::ExprInlineAsm(ast::InlineAsm {
244             asm: token::intern_and_get_ident(asm.get()),
245             asm_str_style: asm_str_style.unwrap(),
246             clobbers: token::intern_and_get_ident(cons.as_slice()),
247             inputs: inputs,
248             outputs: outputs,
249             volatile: volatile,
250             alignstack: alignstack,
251             dialect: dialect
252         }),
253         span: sp
254     })
255 }