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