rust/src/libsyntax_ext/asm.rs

270 lines
9.6 KiB
Rust
Raw Normal View History

// 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.
*/
use self::State::*;
2013-03-11 00:08:38 -05:00
use syntax::ast;
use syntax::codemap;
use syntax::ext::base;
use syntax::ext::base::*;
use syntax::feature_gate;
use syntax::parse::token::intern;
2015-02-01 01:59:46 -06:00
use syntax::parse::{self, token};
use syntax::ptr::P;
use syntax::ast::AsmDialect;
use syntax_pos::Span;
use syntax::tokenstream;
enum State {
Asm,
Outputs,
Inputs,
Clobbers,
Options,
StateNone
}
impl State {
fn next(&self) -> State {
match *self {
Asm => Outputs,
Outputs => Inputs,
Inputs => Clobbers,
Clobbers => Options,
Options => StateNone,
StateNone => StateNone
}
}
}
2013-03-11 00:08:38 -05:00
const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[tokenstream::TokenTree])
-> Box<base::MacResult+'cx> {
2015-02-15 15:14:03 -06:00
if !cx.ecfg.enable_asm() {
feature_gate::emit_feature_err(
&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 {
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("");
let mut asm_str_style = None;
let mut outputs = Vec::new();
let mut inputs = Vec::new();
let mut clobs = Vec::new();
let mut volatile = false;
2013-03-12 03:02:58 -05:00
let mut alignstack = false;
let mut dialect = AsmDialect::Att;
let mut state = Asm;
'statement: loop {
match state {
Asm => {
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()),
"inline assembly must be a string literal") {
Some((s, st)) => (s, st),
// let compilation continue
None => return DummyResult::expr(sp),
};
2015-02-01 01:59:46 -06:00
// This is most likely malformed.
if p2.token != token::Eof {
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());
}
asm = s;
asm_str_style = Some(style);
}
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
if !outputs.is_empty() {
p.eat(&token::Comma);
}
2013-03-12 02:09:53 -05:00
let (constraint, _str_style) = panictry!(p.parse_str());
let span = p.last_span;
panictry!(p.expect(&token::OpenDelim(token::Paren)));
let out = panictry!(p.parse_expr());
panictry!(p.expect(&token::CloseDelim(token::Paren)));
// 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.)
std: Stabilize APIs for the 1.9 release This commit applies all stabilizations, renamings, and deprecations that the library team has decided on for the upcoming 1.9 release. All tracking issues have gone through a cycle-long "final comment period" and the specific APIs stabilized/deprecated are: Stable * `std::panic` * `std::panic::catch_unwind` (renamed from `recover`) * `std::panic::resume_unwind` (renamed from `propagate`) * `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`) * `std::panic::UnwindSafe` (renamed from `RecoverSafe`) * `str::is_char_boundary` * `<*const T>::as_ref` * `<*mut T>::as_ref` * `<*mut T>::as_mut` * `AsciiExt::make_ascii_uppercase` * `AsciiExt::make_ascii_lowercase` * `char::decode_utf16` * `char::DecodeUtf16` * `char::DecodeUtf16Error` * `char::DecodeUtf16Error::unpaired_surrogate` * `BTreeSet::take` * `BTreeSet::replace` * `BTreeSet::get` * `HashSet::take` * `HashSet::replace` * `HashSet::get` * `OsString::with_capacity` * `OsString::clear` * `OsString::capacity` * `OsString::reserve` * `OsString::reserve_exact` * `OsStr::is_empty` * `OsStr::len` * `std::os::unix::thread` * `RawPthread` * `JoinHandleExt` * `JoinHandleExt::as_pthread_t` * `JoinHandleExt::into_pthread_t` * `HashSet::hasher` * `HashMap::hasher` * `CommandExt::exec` * `File::try_clone` * `SocketAddr::set_ip` * `SocketAddr::set_port` * `SocketAddrV4::set_ip` * `SocketAddrV4::set_port` * `SocketAddrV6::set_ip` * `SocketAddrV6::set_port` * `SocketAddrV6::set_flowinfo` * `SocketAddrV6::set_scope_id` * `<[T]>::copy_from_slice` * `ptr::read_volatile` * `ptr::write_volatile` * The `#[deprecated]` attribute * `OpenOptions::create_new` Deprecated * `std::raw::Slice` - use raw parts of `slice` module instead * `std::raw::Repr` - use raw parts of `slice` module instead * `str::char_range_at` - use slicing plus `chars()` plus `len_utf8` * `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8` * `str::char_at` - use slicing plus `chars()` * `str::char_at_reverse` - use slicing plus `chars().rev()` * `str::slice_shift_char` - use `chars()` plus `Chars::as_str` * `CommandExt::session_leader` - use `before_exec` instead. Closes #27719 cc #27751 (deprecating the `Slice` bits) Closes #27754 Closes #27780 Closes #27809 Closes #27811 Closes #27830 Closes #28050 Closes #29453 Closes #29791 Closes #29935 Closes #30014 Closes #30752 Closes #31262 cc #31398 (still need to deal with `before_exec`) Closes #31405 Closes #31572 Closes #31755 Closes #31756
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!(
std: Stabilize APIs for the 1.9 release This commit applies all stabilizations, renamings, and deprecations that the library team has decided on for the upcoming 1.9 release. All tracking issues have gone through a cycle-long "final comment period" and the specific APIs stabilized/deprecated are: Stable * `std::panic` * `std::panic::catch_unwind` (renamed from `recover`) * `std::panic::resume_unwind` (renamed from `propagate`) * `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`) * `std::panic::UnwindSafe` (renamed from `RecoverSafe`) * `str::is_char_boundary` * `<*const T>::as_ref` * `<*mut T>::as_ref` * `<*mut T>::as_mut` * `AsciiExt::make_ascii_uppercase` * `AsciiExt::make_ascii_lowercase` * `char::decode_utf16` * `char::DecodeUtf16` * `char::DecodeUtf16Error` * `char::DecodeUtf16Error::unpaired_surrogate` * `BTreeSet::take` * `BTreeSet::replace` * `BTreeSet::get` * `HashSet::take` * `HashSet::replace` * `HashSet::get` * `OsString::with_capacity` * `OsString::clear` * `OsString::capacity` * `OsString::reserve` * `OsString::reserve_exact` * `OsStr::is_empty` * `OsStr::len` * `std::os::unix::thread` * `RawPthread` * `JoinHandleExt` * `JoinHandleExt::as_pthread_t` * `JoinHandleExt::into_pthread_t` * `HashSet::hasher` * `HashMap::hasher` * `CommandExt::exec` * `File::try_clone` * `SocketAddr::set_ip` * `SocketAddr::set_port` * `SocketAddrV4::set_ip` * `SocketAddrV4::set_port` * `SocketAddrV6::set_ip` * `SocketAddrV6::set_port` * `SocketAddrV6::set_flowinfo` * `SocketAddrV6::set_scope_id` * `<[T]>::copy_from_slice` * `ptr::read_volatile` * `ptr::write_volatile` * The `#[deprecated]` attribute * `OpenOptions::create_new` Deprecated * `std::raw::Slice` - use raw parts of `slice` module instead * `std::raw::Repr` - use raw parts of `slice` module instead * `str::char_range_at` - use slicing plus `chars()` plus `len_utf8` * `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8` * `str::char_at` - use slicing plus `chars()` * `str::char_at_reverse` - use slicing plus `chars().rev()` * `str::slice_shift_char` - use `chars()` plus `Chars::as_str` * `CommandExt::session_leader` - use `before_exec` instead. Closes #27719 cc #27751 (deprecating the `Slice` bits) Closes #27754 Closes #27780 Closes #27809 Closes #27811 Closes #27830 Closes #28050 Closes #29453 Closes #29791 Closes #29935 Closes #30014 Closes #30752 Closes #31262 cc #31398 (still need to deal with `before_exec`) Closes #31405 Closes #31572 Closes #31755 Closes #31756
2016-04-07 12:42:53 -05:00
"={}", ch.as_str())))
}
_ => {
cx.span_err(span, "output operand constraint lacks '=' or '+'");
None
}
};
let is_rw = output.is_some();
let is_indirect = constraint.contains("*");
outputs.push(ast::InlineAsmOutput {
std: Stabilize APIs for the 1.9 release This commit applies all stabilizations, renamings, and deprecations that the library team has decided on for the upcoming 1.9 release. All tracking issues have gone through a cycle-long "final comment period" and the specific APIs stabilized/deprecated are: Stable * `std::panic` * `std::panic::catch_unwind` (renamed from `recover`) * `std::panic::resume_unwind` (renamed from `propagate`) * `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`) * `std::panic::UnwindSafe` (renamed from `RecoverSafe`) * `str::is_char_boundary` * `<*const T>::as_ref` * `<*mut T>::as_ref` * `<*mut T>::as_mut` * `AsciiExt::make_ascii_uppercase` * `AsciiExt::make_ascii_lowercase` * `char::decode_utf16` * `char::DecodeUtf16` * `char::DecodeUtf16Error` * `char::DecodeUtf16Error::unpaired_surrogate` * `BTreeSet::take` * `BTreeSet::replace` * `BTreeSet::get` * `HashSet::take` * `HashSet::replace` * `HashSet::get` * `OsString::with_capacity` * `OsString::clear` * `OsString::capacity` * `OsString::reserve` * `OsString::reserve_exact` * `OsStr::is_empty` * `OsStr::len` * `std::os::unix::thread` * `RawPthread` * `JoinHandleExt` * `JoinHandleExt::as_pthread_t` * `JoinHandleExt::into_pthread_t` * `HashSet::hasher` * `HashMap::hasher` * `CommandExt::exec` * `File::try_clone` * `SocketAddr::set_ip` * `SocketAddr::set_port` * `SocketAddrV4::set_ip` * `SocketAddrV4::set_port` * `SocketAddrV6::set_ip` * `SocketAddrV6::set_port` * `SocketAddrV6::set_flowinfo` * `SocketAddrV6::set_scope_id` * `<[T]>::copy_from_slice` * `ptr::read_volatile` * `ptr::write_volatile` * The `#[deprecated]` attribute * `OpenOptions::create_new` Deprecated * `std::raw::Slice` - use raw parts of `slice` module instead * `std::raw::Repr` - use raw parts of `slice` module instead * `str::char_range_at` - use slicing plus `chars()` plus `len_utf8` * `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8` * `str::char_at` - use slicing plus `chars()` * `str::char_at_reverse` - use slicing plus `chars().rev()` * `str::slice_shift_char` - use `chars()` plus `Chars::as_str` * `CommandExt::session_leader` - use `before_exec` instead. Closes #27719 cc #27751 (deprecating the `Slice` bits) Closes #27754 Closes #27780 Closes #27809 Closes #27811 Closes #27830 Closes #28050 Closes #29453 Closes #29791 Closes #29935 Closes #30014 Closes #30752 Closes #31262 cc #31398 (still need to deal with `before_exec`) Closes #31405 Closes #31572 Closes #31755 Closes #31756
2016-04-07 12:42:53 -05:00
constraint: output.unwrap_or(constraint.clone()),
expr: out,
is_rw: is_rw,
is_indirect: is_indirect,
});
}
}
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
if !inputs.is_empty() {
p.eat(&token::Comma);
}
2013-03-12 02:09:53 -05:00
let (constraint, _str_style) = panictry!(p.parse_str());
if constraint.starts_with("=") {
cx.span_err(p.last_span, "input operand constraint contains '='");
} else if constraint.starts_with("+") {
cx.span_err(p.last_span, "input operand constraint contains '+'");
}
panictry!(p.expect(&token::OpenDelim(token::Paren)));
let input = panictry!(p.parse_expr());
panictry!(p.expect(&token::CloseDelim(token::Paren)));
inputs.push((constraint, input));
}
}
Clobbers => {
2014-10-27 03:22:52 -05:00
while p.token != token::Eof &&
p.token != token::Colon &&
p.token != token::ModSep {
if !clobs.is_empty() {
p.eat(&token::Comma);
}
let (s, _str_style) = panictry!(p.parse_str());
if OPTIONS.iter().any(|&opt| s == opt) {
cx.span_warn(p.last_span, "expected a clobber, found an option");
} else if s.starts_with("{") || s.ends_with("}") {
cx.span_err(p.last_span, "clobber should not be surrounded by braces");
}
clobs.push(s);
}
}
Options => {
let (option, _str_style) = panictry!(p.parse_str());
2013-03-12 02:09:53 -05:00
if option == "volatile" {
// Indicates that the inline assembly has side effects
// and must not be optimized out along with its outputs.
volatile = true;
} else if option == "alignstack" {
2013-03-12 03:02:58 -05:00
alignstack = true;
} else if option == "intel" {
dialect = AsmDialect::Intel;
} else {
cx.span_warn(p.last_span, "unrecognized option");
}
2014-10-27 03:22:52 -05:00
if p.token == token::Comma {
p.eat(&token::Comma);
}
}
StateNone => ()
}
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) => {
p.bump();
break 'statement;
}
2014-10-27 03:22:52 -05:00
(&token::Colon, st, _) |
(&token::ModSep, _, st) => {
p.bump();
state = st;
}
2014-10-27 03:22:52 -05:00
(&token::Eof, _, _) => break 'statement,
_ => break
}
}
2013-03-11 00:08:38 -05:00
}
let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
call_site: sp,
callee: codemap::NameAndSpan {
2015-08-26 19:11:53 -05:00
format: codemap::MacroBang(intern("asm")),
span: None,
allow_internal_unstable: false,
},
});
MacEager::expr(P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprKind::InlineAsm(ast::InlineAsm {
asm: token::intern_and_get_ident(&asm),
asm_str_style: asm_str_style.unwrap(),
outputs: outputs,
inputs: inputs,
clobbers: clobs,
volatile: volatile,
alignstack: alignstack,
dialect: dialect,
expn_id: expn_id,
}),
span: sp,
attrs: ast::ThinVec::new(),
2014-09-13 11:06:01 -05:00
}))
2013-03-11 00:08:38 -05:00
}