2013-08-06 23:50:23 -05:00
|
|
|
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
2013-03-11 00:08:38 -05:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Inline assembly support.
|
|
|
|
*/
|
2014-11-06 02:05:53 -06:00
|
|
|
use self::State::*;
|
2013-03-11 00:08:38 -05:00
|
|
|
|
2015-12-10 08:23:14 -06:00
|
|
|
use syntax::ast;
|
|
|
|
use syntax::codemap;
|
|
|
|
use syntax::ext::base;
|
|
|
|
use syntax::ext::base::*;
|
|
|
|
use syntax::feature_gate;
|
2015-12-18 11:00:19 -06:00
|
|
|
use syntax::parse::token::intern;
|
2015-02-01 01:59:46 -06:00
|
|
|
use syntax::parse::{self, token};
|
2015-12-10 08:23:14 -06:00
|
|
|
use syntax::ptr::P;
|
2015-09-21 04:45:04 -05:00
|
|
|
use syntax::ast::AsmDialect;
|
2016-06-21 17:08:13 -05:00
|
|
|
use syntax_pos::Span;
|
2016-06-20 10:49:33 -05:00
|
|
|
use syntax::tokenstream;
|
2014-06-11 21:33:52 -05:00
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
enum State {
|
|
|
|
Asm,
|
|
|
|
Outputs,
|
|
|
|
Inputs,
|
|
|
|
Clobbers,
|
2014-03-09 17:41:18 -05:00
|
|
|
Options,
|
|
|
|
StateNone
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
|
2014-03-09 17:41:18 -05:00
|
|
|
impl State {
|
|
|
|
fn next(&self) -> State {
|
|
|
|
match *self {
|
|
|
|
Asm => Outputs,
|
|
|
|
Outputs => Inputs,
|
|
|
|
Inputs => Clobbers,
|
|
|
|
Clobbers => Options,
|
|
|
|
Options => StateNone,
|
|
|
|
StateNone => StateNone
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
}
|
2013-03-11 00:08:38 -05:00
|
|
|
|
2015-02-27 08:36:53 -06:00
|
|
|
const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
|
2014-03-09 17:41:18 -05:00
|
|
|
|
2016-06-20 10:49:33 -05:00
|
|
|
pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
|
2014-08-27 20:46:52 -05:00
|
|
|
-> Box<base::MacResult+'cx> {
|
2015-02-15 15:14:03 -06:00
|
|
|
if !cx.ecfg.enable_asm() {
|
|
|
|
feature_gate::emit_feature_err(
|
2015-09-04 18:37:22 -05:00
|
|
|
&cx.parse_sess.span_diagnostic, "asm", sp,
|
|
|
|
feature_gate::GateIssue::Language,
|
|
|
|
feature_gate::EXPLAIN_ASM);
|
2015-02-15 15:14:03 -06:00
|
|
|
return DummyResult::expr(sp);
|
|
|
|
}
|
|
|
|
|
2015-02-01 01:59:46 -06:00
|
|
|
// Split the tts before the first colon, to avoid `asm!("x": y)` being
|
|
|
|
// parsed as `asm!(z)` with `z = "x": y` which is type ascription.
|
|
|
|
let first_colon = tts.iter().position(|tt| {
|
|
|
|
match *tt {
|
2016-06-20 10:49:33 -05:00
|
|
|
tokenstream::TokenTree::Token(_, token::Colon) |
|
|
|
|
tokenstream::TokenTree::Token(_, token::ModSep) => true,
|
2015-02-01 01:59:46 -06:00
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}).unwrap_or(tts.len());
|
|
|
|
let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
|
|
|
|
let mut asm = token::InternedString::new("");
|
2013-10-07 19:49:10 -05:00
|
|
|
let mut asm_str_style = None;
|
2014-02-28 15:09:09 -06:00
|
|
|
let mut outputs = Vec::new();
|
|
|
|
let mut inputs = Vec::new();
|
2014-11-29 20:56:31 -06:00
|
|
|
let mut clobs = Vec::new();
|
2013-03-12 02:01:09 -05:00
|
|
|
let mut volatile = false;
|
2013-03-12 03:02:58 -05:00
|
|
|
let mut alignstack = false;
|
2015-09-21 04:45:04 -05:00
|
|
|
let mut dialect = AsmDialect::Att;
|
2013-03-12 02:01:09 -05:00
|
|
|
|
|
|
|
let mut state = Asm;
|
2013-04-26 18:19:26 -05:00
|
|
|
|
2014-03-09 17:41:18 -05:00
|
|
|
'statement: loop {
|
2013-03-12 02:01:09 -05:00
|
|
|
match state {
|
|
|
|
Asm => {
|
2015-01-14 09:02:20 -06:00
|
|
|
if asm_str_style.is_some() {
|
|
|
|
// If we already have a string with instructions,
|
|
|
|
// ending up in Asm state again is an error.
|
|
|
|
cx.span_err(sp, "malformed inline assembly");
|
|
|
|
return DummyResult::expr(sp);
|
|
|
|
}
|
2015-02-01 01:59:46 -06:00
|
|
|
// Nested parser, stop before the first colon (see above).
|
|
|
|
let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
|
|
|
|
let (s, style) = match expr_to_string(cx, panictry!(p2.parse_expr()),
|
2014-11-28 00:01:41 -06:00
|
|
|
"inline assembly must be a string literal") {
|
2014-01-17 08:53:10 -06:00
|
|
|
Some((s, st)) => (s, st),
|
|
|
|
// let compilation continue
|
2014-04-15 07:00:14 -05:00
|
|
|
None => return DummyResult::expr(sp),
|
2014-01-17 08:53:10 -06:00
|
|
|
};
|
2015-02-01 01:59:46 -06:00
|
|
|
|
|
|
|
// This is most likely malformed.
|
|
|
|
if p2.token != token::Eof {
|
2015-12-02 20:37:48 -06:00
|
|
|
let mut extra_tts = panictry!(p2.parse_all_token_trees());
|
2015-02-01 01:59:46 -06:00
|
|
|
extra_tts.extend(tts[first_colon..].iter().cloned());
|
|
|
|
p = parse::tts_to_parser(cx.parse_sess, extra_tts, cx.cfg());
|
|
|
|
}
|
|
|
|
|
2013-10-07 19:49:10 -05:00
|
|
|
asm = s;
|
|
|
|
asm_str_style = Some(style);
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
Outputs => {
|
2014-10-27 03:22:52 -05:00
|
|
|
while p.token != token::Eof &&
|
|
|
|
p.token != token::Colon &&
|
|
|
|
p.token != token::ModSep {
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2015-03-24 18:54:09 -05:00
|
|
|
if !outputs.is_empty() {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.eat(&token::Comma);
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2015-03-28 16:58:51 -05:00
|
|
|
let (constraint, _str_style) = panictry!(p.parse_str());
|
2013-10-17 13:24:41 -05:00
|
|
|
|
2014-03-09 17:41:18 -05:00
|
|
|
let span = p.last_span;
|
2013-10-17 13:24:41 -05:00
|
|
|
|
2015-03-28 16:58:51 -05:00
|
|
|
panictry!(p.expect(&token::OpenDelim(token::Paren)));
|
2015-11-10 18:08:26 -06:00
|
|
|
let out = panictry!(p.parse_expr());
|
2015-03-28 16:58:51 -05:00
|
|
|
panictry!(p.expect(&token::CloseDelim(token::Paren)));
|
2013-03-12 02:01:09 -05:00
|
|
|
|
2014-03-09 17:41:18 -05:00
|
|
|
// Expands a read+write operand into two operands.
|
|
|
|
//
|
|
|
|
// Use '+' modifier when you want the same expression
|
|
|
|
// to be both an input and an output at the same time.
|
|
|
|
// It's the opposite of '=&' which means that the memory
|
|
|
|
// cannot be shared with any other operand (usually when
|
|
|
|
// a register is clobbered early.)
|
2016-04-07 12:42:53 -05:00
|
|
|
let mut ch = constraint.chars();
|
|
|
|
let output = match ch.next() {
|
|
|
|
Some('=') => None,
|
|
|
|
Some('+') => {
|
2015-01-07 10:58:31 -06:00
|
|
|
Some(token::intern_and_get_ident(&format!(
|
2016-04-07 12:42:53 -05:00
|
|
|
"={}", ch.as_str())))
|
2014-03-09 17:41:18 -05:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
cx.span_err(span, "output operand constraint lacks '=' or '+'");
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-19 14:39:26 -05:00
|
|
|
let is_rw = output.is_some();
|
2015-11-06 07:31:02 -06:00
|
|
|
let is_indirect = constraint.contains("*");
|
2015-12-05 02:18:24 -06:00
|
|
|
outputs.push(ast::InlineAsmOutput {
|
2016-04-07 12:42:53 -05:00
|
|
|
constraint: output.unwrap_or(constraint.clone()),
|
2015-12-05 02:18:24 -06:00
|
|
|
expr: out,
|
|
|
|
is_rw: is_rw,
|
|
|
|
is_indirect: is_indirect,
|
|
|
|
});
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Inputs => {
|
2014-10-27 03:22:52 -05:00
|
|
|
while p.token != token::Eof &&
|
|
|
|
p.token != token::Colon &&
|
|
|
|
p.token != token::ModSep {
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2015-03-24 18:54:09 -05:00
|
|
|
if !inputs.is_empty() {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.eat(&token::Comma);
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2015-03-28 16:58:51 -05:00
|
|
|
let (constraint, _str_style) = panictry!(p.parse_str());
|
2013-10-17 13:24:41 -05:00
|
|
|
|
2015-11-06 07:31:02 -06:00
|
|
|
if constraint.starts_with("=") {
|
2013-12-30 17:30:14 -06:00
|
|
|
cx.span_err(p.last_span, "input operand constraint contains '='");
|
2015-11-06 07:31:02 -06:00
|
|
|
} else if constraint.starts_with("+") {
|
2013-12-30 17:30:14 -06:00
|
|
|
cx.span_err(p.last_span, "input operand constraint contains '+'");
|
2013-10-17 13:24:41 -05:00
|
|
|
}
|
|
|
|
|
2015-03-28 16:58:51 -05:00
|
|
|
panictry!(p.expect(&token::OpenDelim(token::Paren)));
|
2015-11-10 18:08:26 -06:00
|
|
|
let input = panictry!(p.parse_expr());
|
2015-03-28 16:58:51 -05:00
|
|
|
panictry!(p.expect(&token::CloseDelim(token::Paren)));
|
2013-03-12 02:01:09 -05:00
|
|
|
|
2013-07-31 16:59:59 -05:00
|
|
|
inputs.push((constraint, input));
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
}
|
2014-05-28 11:24:28 -05:00
|
|
|
Clobbers => {
|
2014-10-27 03:22:52 -05:00
|
|
|
while p.token != token::Eof &&
|
|
|
|
p.token != token::Colon &&
|
|
|
|
p.token != token::ModSep {
|
2014-05-28 11:24:28 -05:00
|
|
|
|
2015-03-24 18:54:09 -05:00
|
|
|
if !clobs.is_empty() {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.eat(&token::Comma);
|
2014-05-28 11:24:28 -05:00
|
|
|
}
|
|
|
|
|
2015-03-28 16:58:51 -05:00
|
|
|
let (s, _str_style) = panictry!(p.parse_str());
|
2014-05-28 11:24:28 -05:00
|
|
|
|
2014-11-20 19:25:27 -06:00
|
|
|
if OPTIONS.iter().any(|&opt| s == opt) {
|
2014-08-23 05:41:32 -05:00
|
|
|
cx.span_warn(p.last_span, "expected a clobber, found an option");
|
2016-07-06 07:54:31 -05:00
|
|
|
} else if s.starts_with("{") || s.ends_with("}") {
|
|
|
|
cx.span_err(p.last_span, "clobber should not be surrounded by braces");
|
2014-05-28 11:24:28 -05:00
|
|
|
}
|
2016-07-06 07:54:31 -05:00
|
|
|
|
2014-11-29 20:56:31 -06:00
|
|
|
clobs.push(s);
|
2014-05-28 11:24:28 -05:00
|
|
|
}
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
Options => {
|
2015-03-28 16:58:51 -05:00
|
|
|
let (option, _str_style) = panictry!(p.parse_str());
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2014-11-20 19:25:27 -06:00
|
|
|
if option == "volatile" {
|
2014-03-09 17:41:18 -05:00
|
|
|
// Indicates that the inline assembly has side effects
|
|
|
|
// and must not be optimized out along with its outputs.
|
2013-03-12 02:01:09 -05:00
|
|
|
volatile = true;
|
2014-11-20 19:25:27 -06:00
|
|
|
} else if option == "alignstack" {
|
2013-03-12 03:02:58 -05:00
|
|
|
alignstack = true;
|
2014-11-20 19:25:27 -06:00
|
|
|
} else if option == "intel" {
|
2015-09-21 04:45:04 -05:00
|
|
|
dialect = AsmDialect::Intel;
|
2014-03-09 17:41:18 -05:00
|
|
|
} else {
|
|
|
|
cx.span_warn(p.last_span, "unrecognized option");
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
|
2014-10-27 03:22:52 -05:00
|
|
|
if p.token == token::Comma {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.eat(&token::Comma);
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
}
|
2014-03-09 17:41:18 -05:00
|
|
|
StateNone => ()
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
|
2014-03-09 17:41:18 -05:00
|
|
|
loop {
|
|
|
|
// MOD_SEP is a double colon '::' without space in between.
|
|
|
|
// When encountered, the state must be advanced twice.
|
|
|
|
match (&p.token, state.next(), state.next().next()) {
|
2014-10-27 03:22:52 -05:00
|
|
|
(&token::Colon, StateNone, _) |
|
|
|
|
(&token::ModSep, _, StateNone) => {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.bump();
|
2014-03-09 17:41:18 -05:00
|
|
|
break 'statement;
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2014-10-27 03:22:52 -05:00
|
|
|
(&token::Colon, st, _) |
|
|
|
|
(&token::ModSep, _, st) => {
|
2015-12-30 17:11:53 -06:00
|
|
|
p.bump();
|
2014-03-09 17:41:18 -05:00
|
|
|
state = st;
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2014-10-27 03:22:52 -05:00
|
|
|
(&token::Eof, _, _) => break 'statement,
|
2014-03-09 17:41:18 -05:00
|
|
|
_ => break
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2013-03-11 00:08:38 -05:00
|
|
|
}
|
|
|
|
|
2014-09-28 11:25:48 -05:00
|
|
|
let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
|
2014-09-27 03:33:36 -05:00
|
|
|
call_site: sp,
|
|
|
|
callee: codemap::NameAndSpan {
|
2015-08-26 19:11:53 -05:00
|
|
|
format: codemap::MacroBang(intern("asm")),
|
2014-09-27 03:33:36 -05:00
|
|
|
span: None,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-02-28 21:09:28 -06:00
|
|
|
allow_internal_unstable: false,
|
2014-09-27 03:33:36 -05:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2015-02-27 13:14:42 -06:00
|
|
|
MacEager::expr(P(ast::Expr {
|
2013-09-06 21:11:55 -05:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
2016-02-08 09:05:05 -06:00
|
|
|
node: ast::ExprKind::InlineAsm(ast::InlineAsm {
|
2015-02-04 14:48:12 -06:00
|
|
|
asm: token::intern_and_get_ident(&asm),
|
2013-10-07 19:49:10 -05:00
|
|
|
asm_str_style: asm_str_style.unwrap(),
|
2013-03-27 15:42:21 -05:00
|
|
|
outputs: outputs,
|
2014-08-19 14:39:26 -05:00
|
|
|
inputs: inputs,
|
2014-11-29 20:56:31 -06:00
|
|
|
clobbers: clobs,
|
2013-03-27 15:42:21 -05:00
|
|
|
volatile: volatile,
|
2013-03-27 16:42:19 -05:00
|
|
|
alignstack: alignstack,
|
2014-09-27 03:33:36 -05:00
|
|
|
dialect: dialect,
|
|
|
|
expn_id: expn_id,
|
2013-03-27 15:42:21 -05:00
|
|
|
}),
|
2015-11-03 10:39:51 -06:00
|
|
|
span: sp,
|
2016-06-17 23:01:57 -05:00
|
|
|
attrs: ast::ThinVec::new(),
|
2014-09-13 11:06:01 -05:00
|
|
|
}))
|
2013-03-11 00:08:38 -05:00
|
|
|
}
|