1 // Llvm-style inline assembly support.
7 use rustc_ast
::token
::{self, Token}
;
8 use rustc_ast
::tokenstream
::{self, TokenStream}
;
9 use rustc_ast
::LlvmAsmDialect
;
10 use rustc_errors
::{struct_span_err, DiagnosticBuilder, PResult}
;
11 use rustc_expand
::base
::*;
12 use rustc_parse
::parser
::Parser
;
13 use rustc_span
::symbol
::{kw, sym, Symbol}
;
26 fn next(&self) -> State
{
33 StateNone
=> StateNone
,
38 const OPTIONS
: &[Symbol
] = &[sym
::volatile
, sym
::alignstack
, sym
::intel
];
40 pub fn expand_llvm_asm
<'cx
>(
41 cx
: &'cx
mut ExtCtxt
<'_
>,
44 ) -> Box
<dyn MacResult
+ 'cx
> {
45 let mut inline_asm
= match parse_inline_asm(cx
, sp
, tts
) {
46 Ok(Some(inline_asm
)) => inline_asm
,
47 Ok(None
) => return DummyResult
::any(sp
),
50 return DummyResult
::any(sp
);
54 // If there are no outputs, the inline assembly is executed just for its side effects,
55 // so ensure that it is volatile
56 if inline_asm
.outputs
.is_empty() {
57 inline_asm
.volatile
= true;
60 MacEager
::expr(P(ast
::Expr
{
61 id
: ast
::DUMMY_NODE_ID
,
62 kind
: ast
::ExprKind
::LlvmInlineAsm(P(inline_asm
)),
63 span
: cx
.with_def_site_ctxt(sp
),
64 attrs
: ast
::AttrVec
::new(),
69 fn parse_asm_str
<'a
>(p
: &mut Parser
<'a
>) -> PResult
<'a
, Symbol
> {
70 match p
.parse_str_lit() {
71 Ok(str_lit
) => Ok(str_lit
.symbol_unescaped
),
73 let span
= opt_lit
.map_or(p
.token
.span
, |lit
| lit
.span
);
74 let mut err
= p
.sess
.span_diagnostic
.struct_span_err(span
, "expected string literal");
75 err
.span_label(span
, "not a string literal");
81 fn parse_inline_asm
<'a
>(
85 ) -> Result
<Option
<ast
::LlvmInlineAsm
>, DiagnosticBuilder
<'a
>> {
86 // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being
87 // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription.
93 tokenstream
::TokenTree
::Token(Token { kind: token::Colon | token::ModSep, .. }
)
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
::Empty
;
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
= LlvmAsmDialect
::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
.sess
.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_token
.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()))),
177 cx
.sess
.parse_sess
.span_diagnostic
,
180 "output operand constraint lacks '=' or '+'"
187 let is_rw
= output
.is_some();
188 let is_indirect
= constraint_str
.contains('
*'
);
189 outputs
.push(ast
::LlvmInlineAsmOutput
{
190 constraint
: output
.unwrap_or(constraint
),
198 while p
.token
!= token
::Eof
&& p
.token
!= token
::Colon
&& p
.token
!= token
::ModSep
{
199 if !inputs
.is_empty() {
200 p
.eat(&token
::Comma
);
203 let constraint
= parse_asm_str(&mut p
)?
;
205 if constraint
.as_str().starts_with('
='
) {
207 cx
.sess
.parse_sess
.span_diagnostic
,
210 "input operand constraint contains '='"
213 } else if constraint
.as_str().starts_with('
+'
) {
215 cx
.sess
.parse_sess
.span_diagnostic
,
218 "input operand constraint contains '+'"
223 p
.expect(&token
::OpenDelim(token
::Paren
))?
;
224 let input
= p
.parse_expr()?
;
225 p
.expect(&token
::CloseDelim(token
::Paren
))?
;
227 inputs
.push((constraint
, input
));
231 while p
.token
!= token
::Eof
&& p
.token
!= token
::Colon
&& p
.token
!= token
::ModSep
{
232 if !clobs
.is_empty() {
233 p
.eat(&token
::Comma
);
236 let s
= parse_asm_str(&mut p
)?
;
238 if OPTIONS
.iter().any(|&opt
| s
== opt
) {
239 cx
.span_warn(p
.prev_token
.span
, "expected a clobber, found an option");
240 } else if s
.as_str().starts_with('{') || s.as_str().ends_with('}'
) {
242 cx
.sess
.parse_sess
.span_diagnostic
,
245 "clobber should not be surrounded by braces"
254 let option
= parse_asm_str(&mut p
)?
;
256 if option
== sym
::volatile
{
257 // Indicates that the inline assembly has side effects
258 // and must not be optimized out along with its outputs.
260 } else if option
== sym
::alignstack
{
262 } else if option
== sym
::intel
{
263 dialect
= LlvmAsmDialect
::Intel
;
265 cx
.span_warn(p
.prev_token
.span
, "unrecognized option");
268 if p
.token
== token
::Comma
{
269 p
.eat(&token
::Comma
);
276 // MOD_SEP is a double colon '::' without space in between.
277 // When encountered, the state must be advanced twice.
278 match (&p
.token
.kind
, state
.next(), state
.next().next()) {
279 (&token
::Colon
, StateNone
, _
) | (&token
::ModSep
, _
, StateNone
) => {
283 (&token
::Colon
, st
, _
) | (&token
::ModSep
, _
, st
) => {
287 (&token
::Eof
, ..) => break 'statement
,
293 Ok(Some(ast
::LlvmInlineAsm
{
295 asm_str_style
: asm_str_style
.unwrap(),