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