rust/compiler/rustc_builtin_macros/src/llvm_asm.rs

302 lines
11 KiB
Rust
Raw Normal View History

// Llvm-style inline assembly support.
2016-06-06 09:52:48 -05:00
//
2019-02-04 06:49:54 -06:00
use State::*;
2013-03-11 00:08:38 -05:00
2020-04-27 12:56:11 -05:00
use rustc_ast as ast;
use rustc_ast::ptr::P;
use rustc_ast::token::{self, Token};
use rustc_ast::tokenstream::{self, TokenStream};
2020-04-27 12:56:11 -05:00
use rustc_ast::LlvmAsmDialect;
use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult};
use rustc_expand::base::*;
use rustc_parse::parser::Parser;
2020-01-01 12:30:57 -06:00
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
enum State {
Asm,
Outputs,
Inputs,
Clobbers,
Options,
2016-06-06 09:52:48 -05:00
StateNone,
}
impl State {
fn next(&self) -> State {
match *self {
2016-06-06 09:52:48 -05:00
Asm => Outputs,
Outputs => Inputs,
Inputs => Clobbers,
Clobbers => Options,
Options => StateNone,
StateNone => StateNone,
}
}
}
2013-03-11 00:08:38 -05:00
const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel];
pub fn expand_llvm_asm<'cx>(
2019-12-22 16:42:04 -06:00
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
tts: TokenStream,
) -> Box<dyn MacResult + 'cx> {
let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
Ok(Some(inline_asm)) => inline_asm,
Ok(None) => return DummyResult::any(sp),
Err(mut err) => {
err.emit();
return DummyResult::any(sp);
}
};
// If there are no outputs, the inline assembly is executed just for its side effects,
// so ensure that it is volatile
if inline_asm.outputs.is_empty() {
inline_asm.volatile = true;
}
MacEager::expr(P(ast::Expr {
id: ast::DUMMY_NODE_ID,
kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)),
2019-09-14 15:17:11 -05:00
span: cx.with_def_site_ctxt(sp),
2019-12-03 09:38:34 -06:00
attrs: ast::AttrVec::new(),
2020-05-19 15:56:20 -05:00
tokens: None,
}))
}
fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> {
match p.parse_str_lit() {
Ok(str_lit) => Ok(str_lit.symbol_unescaped),
Err(opt_lit) => {
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
2019-11-16 11:11:05 -06:00
let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
err.span_label(span, "not a string literal");
Err(err)
}
}
}
fn parse_inline_asm<'a>(
cx: &mut ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
) -> Result<Option<ast::LlvmInlineAsm>, DiagnosticBuilder<'a>> {
// Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being
// parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription.
2019-12-22 16:42:04 -06:00
let first_colon = tts
.trees()
.position(|tt| match tt {
tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) => true,
2019-12-22 16:42:04 -06:00
_ => false,
2016-06-06 09:52:48 -05:00
})
.unwrap_or(tts.len());
let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect());
let mut asm = kw::Invalid;
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 = LlvmAsmDialect::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.
return Err(struct_span_err!(
cx.sess.parse_sess.span_diagnostic,
sp,
E0660,
"malformed inline assembly"
));
}
2015-02-01 01:59:46 -06:00
// Nested parser, stop before the first colon (see above).
2019-12-22 16:42:04 -06:00
let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect());
if p2.token == token::Eof {
let mut err =
cx.struct_span_err(sp, "macro requires a string literal as an argument");
err.span_label(sp, "string literal required");
return Err(err);
}
let expr = p2.parse_expr()?;
let (s, style) =
match expr_to_string(cx, expr, "inline assembly must be a string literal") {
Some((s, st)) => (s, st),
None => return Ok(None),
};
2015-02-01 01:59:46 -06:00
// This is most likely malformed.
if p2.token != token::Eof {
let mut extra_tts = p2.parse_all_token_trees()?;
extra_tts.extend(tts.trees().skip(first_colon));
p = cx.new_parser_from_tts(extra_tts.into_iter().collect());
2015-02-01 01:59:46 -06:00
}
asm = s;
asm_str_style = Some(style);
}
Outputs => {
2016-06-06 09:52:48 -05:00
while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
if !outputs.is_empty() {
p.eat(&token::Comma);
}
2013-03-12 02:09:53 -05:00
let constraint = parse_asm_str(&mut p)?;
let span = p.prev_token.span;
p.expect(&token::OpenDelim(token::Paren))?;
let expr = p.parse_expr()?;
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.)
let constraint_str = constraint.as_str();
let mut ch = constraint_str.chars();
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 output = match ch.next() {
Some('=') => None,
2019-12-22 16:42:04 -06:00
Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
_ => {
struct_span_err!(
cx.sess.parse_sess.span_diagnostic,
2019-12-22 16:42:04 -06:00
span,
E0661,
"output operand constraint lacks '=' or '+'"
)
.emit();
None
}
};
let is_rw = output.is_some();
let is_indirect = constraint_str.contains('*');
outputs.push(ast::LlvmInlineAsmOutput {
constraint: output.unwrap_or(constraint),
expr,
is_rw,
is_indirect,
});
}
}
Inputs => {
2016-06-06 09:52:48 -05:00
while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
if !inputs.is_empty() {
p.eat(&token::Comma);
}
2013-03-12 02:09:53 -05:00
let constraint = parse_asm_str(&mut p)?;
if constraint.as_str().starts_with('=') {
struct_span_err!(
cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
E0662,
"input operand constraint contains '='"
)
.emit();
} else if constraint.as_str().starts_with('+') {
struct_span_err!(
cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
E0663,
"input operand constraint contains '+'"
)
.emit();
}
p.expect(&token::OpenDelim(token::Paren))?;
let input = p.parse_expr()?;
p.expect(&token::CloseDelim(token::Paren))?;
inputs.push((constraint, input));
}
}
Clobbers => {
2016-06-06 09:52:48 -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 = parse_asm_str(&mut p)?;
if OPTIONS.iter().any(|&opt| s == opt) {
cx.span_warn(p.prev_token.span, "expected a clobber, found an option");
} else if s.as_str().starts_with('{') || s.as_str().ends_with('}') {
struct_span_err!(
cx.sess.parse_sess.span_diagnostic,
p.prev_token.span,
2019-12-22 16:42:04 -06:00
E0664,
"clobber should not be surrounded by braces"
)
.emit();
}
clobs.push(s);
}
}
Options => {
let option = parse_asm_str(&mut p)?;
2013-03-12 02:09:53 -05:00
if option == sym::volatile {
// Indicates that the inline assembly has side effects
// and must not be optimized out along with its outputs.
volatile = true;
} else if option == sym::alignstack {
2013-03-12 03:02:58 -05:00
alignstack = true;
} else if option == sym::intel {
dialect = LlvmAsmDialect::Intel;
} else {
cx.span_warn(p.prev_token.span, "unrecognized option");
}
2014-10-27 03:22:52 -05:00
if p.token == token::Comma {
p.eat(&token::Comma);
}
}
2016-06-06 09:52:48 -05:00
StateNone => (),
}
loop {
// MOD_SEP is a double colon '::' without space in between.
// When encountered, the state must be advanced twice.
2019-06-04 17:17:07 -05:00
match (&p.token.kind, state.next(), state.next().next()) {
2019-12-22 16:42:04 -06:00
(&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => {
p.bump();
break 'statement;
}
2019-12-22 16:42:04 -06:00
(&token::Colon, st, _) | (&token::ModSep, _, st) => {
p.bump();
state = st;
}
2016-08-26 11:23:42 -05:00
(&token::Eof, ..) => break 'statement,
2016-06-06 09:52:48 -05:00
_ => break,
}
}
2013-03-11 00:08:38 -05:00
}
Ok(Some(ast::LlvmInlineAsm {
asm,
asm_str_style: asm_str_style.unwrap(),
outputs,
inputs,
clobbers: clobs,
volatile,
alignstack,
dialect,
2014-09-13 11:06:01 -05:00
}))
2013-03-11 00:08:38 -05:00
}