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