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