1 // Inline assembly support.
5 use errors::{DiagnosticBuilder, PResult};
6 use rustc_expand::base::*;
7 use rustc_parse::parser::Parser;
8 use rustc_span::symbol::{kw, sym, Symbol};
10 use syntax::ast::{self, AsmDialect};
12 use syntax::token::{self, Token};
13 use syntax::tokenstream::{self, TokenStream};
14 use syntax::{span_err, struct_span_err};
16 use rustc_error_codes::*;
28 fn next(&self) -> State {
35 StateNone => StateNone,
40 const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel];
42 pub fn expand_asm<'cx>(
43 cx: &'cx mut ExtCtxt<'_>,
46 ) -> Box<dyn MacResult + 'cx> {
47 let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
48 Ok(Some(inline_asm)) => inline_asm,
49 Ok(None) => return DummyResult::any(sp),
52 return DummyResult::any(sp);
56 // If there are no outputs, the inline assembly is executed just for its side effects,
57 // so ensure that it is volatile
58 if inline_asm.outputs.is_empty() {
59 inline_asm.volatile = true;
62 MacEager::expr(P(ast::Expr {
63 id: ast::DUMMY_NODE_ID,
64 kind: ast::ExprKind::InlineAsm(P(inline_asm)),
65 span: cx.with_def_site_ctxt(sp),
66 attrs: ast::AttrVec::new(),
70 fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> {
71 match p.parse_str_lit() {
72 Ok(str_lit) => Ok(str_lit.symbol_unescaped),
74 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
75 let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
76 err.span_label(span, "not a string literal");
82 fn parse_inline_asm<'a>(
86 ) -> Result<Option<ast::InlineAsm>, DiagnosticBuilder<'a>> {
87 // Split the tts before the first colon, to avoid `asm!("x": y)` being
88 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
91 .position(|tt| match tt {
92 tokenstream::TokenTree::Token(Token { kind: token::Colon, .. })
93 | tokenstream::TokenTree::Token(Token { kind: token::ModSep, .. }) => true,
96 .unwrap_or(tts.len());
97 let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect());
98 let mut asm = kw::Invalid;
99 let mut asm_str_style = None;
100 let mut outputs = Vec::new();
101 let mut inputs = Vec::new();
102 let mut clobs = Vec::new();
103 let mut volatile = false;
104 let mut alignstack = false;
105 let mut dialect = AsmDialect::Att;
112 if asm_str_style.is_some() {
113 // If we already have a string with instructions,
114 // ending up in Asm state again is an error.
115 return Err(struct_span_err!(
116 cx.parse_sess.span_diagnostic,
119 "malformed inline assembly"
122 // Nested parser, stop before the first colon (see above).
123 let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect());
125 if p2.token == token::Eof {
127 cx.struct_span_err(sp, "macro requires a string literal as an argument");
128 err.span_label(sp, "string literal required");
132 let expr = p2.parse_expr()?;
134 match expr_to_string(cx, expr, "inline assembly must be a string literal") {
135 Some((s, st)) => (s, st),
136 None => return Ok(None),
139 // This is most likely malformed.
140 if p2.token != token::Eof {
141 let mut extra_tts = p2.parse_all_token_trees()?;
142 extra_tts.extend(tts.trees().skip(first_colon));
143 p = cx.new_parser_from_tts(extra_tts.into_iter().collect());
147 asm_str_style = Some(style);
150 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
151 if !outputs.is_empty() {
152 p.eat(&token::Comma);
155 let constraint = parse_asm_str(&mut p)?;
157 let span = p.prev_span;
159 p.expect(&token::OpenDelim(token::Paren))?;
160 let expr = p.parse_expr()?;
161 p.expect(&token::CloseDelim(token::Paren))?;
163 // Expands a read+write operand into two operands.
165 // Use '+' modifier when you want the same expression
166 // to be both an input and an output at the same time.
167 // It's the opposite of '=&' which means that the memory
168 // cannot be shared with any other operand (usually when
169 // a register is clobbered early.)
170 let constraint_str = constraint.as_str();
171 let mut ch = constraint_str.chars();
172 let output = match ch.next() {
174 Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
180 "output operand constraint lacks '=' or '+'"
186 let is_rw = output.is_some();
187 let is_indirect = constraint_str.contains("*");
188 outputs.push(ast::InlineAsmOutput {
189 constraint: output.unwrap_or(constraint),
197 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
198 if !inputs.is_empty() {
199 p.eat(&token::Comma);
202 let constraint = parse_asm_str(&mut p)?;
204 if constraint.as_str().starts_with("=") {
205 span_err!(cx, p.prev_span, E0662, "input operand constraint contains '='");
206 } else if constraint.as_str().starts_with("+") {
207 span_err!(cx, p.prev_span, E0663, "input operand constraint contains '+'");
210 p.expect(&token::OpenDelim(token::Paren))?;
211 let input = p.parse_expr()?;
212 p.expect(&token::CloseDelim(token::Paren))?;
214 inputs.push((constraint, input));
218 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
219 if !clobs.is_empty() {
220 p.eat(&token::Comma);
223 let s = parse_asm_str(&mut p)?;
225 if OPTIONS.iter().any(|&opt| s == opt) {
226 cx.span_warn(p.prev_span, "expected a clobber, found an option");
227 } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
232 "clobber should not be surrounded by braces"
240 let option = parse_asm_str(&mut p)?;
242 if option == sym::volatile {
243 // Indicates that the inline assembly has side effects
244 // and must not be optimized out along with its outputs.
246 } else if option == sym::alignstack {
248 } else if option == sym::intel {
249 dialect = AsmDialect::Intel;
251 cx.span_warn(p.prev_span, "unrecognized option");
254 if p.token == token::Comma {
255 p.eat(&token::Comma);
262 // MOD_SEP is a double colon '::' without space in between.
263 // When encountered, the state must be advanced twice.
264 match (&p.token.kind, state.next(), state.next().next()) {
265 (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => {
269 (&token::Colon, st, _) | (&token::ModSep, _, st) => {
273 (&token::Eof, ..) => break 'statement,
279 Ok(Some(ast::InlineAsm {
281 asm_str_style: asm_str_style.unwrap(),