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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
use ast;
|
2013-08-31 11:13:04 -05:00
|
|
|
use codemap::Span;
|
2013-03-11 00:08:38 -05:00
|
|
|
use ext::base;
|
|
|
|
use ext::base::*;
|
2013-03-12 02:01:09 -05:00
|
|
|
use parse;
|
|
|
|
use parse::token;
|
|
|
|
|
|
|
|
enum State {
|
|
|
|
Asm,
|
|
|
|
Outputs,
|
|
|
|
Inputs,
|
|
|
|
Clobbers,
|
|
|
|
Options
|
|
|
|
}
|
|
|
|
|
|
|
|
fn next_state(s: State) -> Option<State> {
|
|
|
|
match s {
|
|
|
|
Asm => Some(Outputs),
|
|
|
|
Outputs => Some(Inputs),
|
|
|
|
Inputs => Some(Clobbers),
|
|
|
|
Clobbers => Some(Options),
|
|
|
|
Options => None
|
|
|
|
}
|
|
|
|
}
|
2013-03-11 00:08:38 -05:00
|
|
|
|
2013-12-28 23:06:22 -06:00
|
|
|
pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::token_tree])
|
2013-03-12 15:00:50 -05:00
|
|
|
-> base::MacResult {
|
2013-12-30 16:04:00 -06:00
|
|
|
let mut p = parse::new_parser_from_tts(cx.parse_sess(),
|
|
|
|
cx.cfg(),
|
|
|
|
tts.to_owned());
|
2013-03-12 02:01:09 -05:00
|
|
|
|
2013-06-12 12:02:55 -05:00
|
|
|
let mut asm = @"";
|
2013-10-07 19:49:10 -05:00
|
|
|
let mut asm_str_style = None;
|
2013-03-12 02:01:09 -05:00
|
|
|
let mut outputs = ~[];
|
|
|
|
let mut inputs = ~[];
|
|
|
|
let mut cons = ~"";
|
|
|
|
let mut volatile = false;
|
2013-03-12 03:02:58 -05:00
|
|
|
let mut alignstack = false;
|
2013-03-27 16:42:19 -05:00
|
|
|
let mut dialect = ast::asm_att;
|
2013-03-12 02:01:09 -05:00
|
|
|
|
|
|
|
let mut state = Asm;
|
2013-04-26 18:19:26 -05:00
|
|
|
|
|
|
|
// Not using labeled break to get us through one round of bootstrapping.
|
2013-09-25 19:56:54 -05:00
|
|
|
let mut continue_ = true;
|
|
|
|
while continue_ {
|
2013-03-12 02:01:09 -05:00
|
|
|
match state {
|
|
|
|
Asm => {
|
2013-10-07 19:49:10 -05:00
|
|
|
let (s, style) =
|
|
|
|
expr_to_str(cx, p.parse_expr(),
|
|
|
|
"inline assembly must be a string literal.");
|
|
|
|
asm = s;
|
|
|
|
asm_str_style = Some(style);
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
Outputs => {
|
2013-12-30 17:09:41 -06:00
|
|
|
while p.token != token::EOF &&
|
|
|
|
p.token != token::COLON &&
|
|
|
|
p.token != token::MOD_SEP {
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
if outputs.len() != 0 {
|
|
|
|
p.eat(&token::COMMA);
|
|
|
|
}
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-10-07 19:49:10 -05:00
|
|
|
let (constraint, _str_style) = p.parse_str();
|
2013-10-17 13:24:41 -05:00
|
|
|
|
|
|
|
if constraint.starts_with("+") {
|
2013-12-30 17:30:14 -06:00
|
|
|
cx.span_unimpl(p.last_span,
|
2013-10-17 13:24:41 -05:00
|
|
|
"'+' (read+write) output operand constraint modifier");
|
|
|
|
} else if !constraint.starts_with("=") {
|
2013-12-30 17:30:14 -06:00
|
|
|
cx.span_err(p.last_span, "output operand constraint lacks '='");
|
2013-10-17 13:24:41 -05:00
|
|
|
}
|
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
p.expect(&token::LPAREN);
|
|
|
|
let out = p.parse_expr();
|
|
|
|
p.expect(&token::RPAREN);
|
|
|
|
|
|
|
|
outputs.push((constraint, out));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Inputs => {
|
2013-12-30 17:09:41 -06:00
|
|
|
while p.token != token::EOF &&
|
|
|
|
p.token != token::COLON &&
|
|
|
|
p.token != token::MOD_SEP {
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
if inputs.len() != 0 {
|
|
|
|
p.eat(&token::COMMA);
|
|
|
|
}
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-10-07 19:49:10 -05:00
|
|
|
let (constraint, _str_style) = p.parse_str();
|
2013-10-17 13:24:41 -05:00
|
|
|
|
|
|
|
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
|
|
|
} 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
|
|
|
}
|
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
p.expect(&token::LPAREN);
|
2013-07-31 16:59:59 -05:00
|
|
|
let input = p.parse_expr();
|
2013-03-12 02:01:09 -05:00
|
|
|
p.expect(&token::RPAREN);
|
|
|
|
|
2013-07-31 16:59:59 -05:00
|
|
|
inputs.push((constraint, input));
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Clobbers => {
|
|
|
|
let mut clobs = ~[];
|
2013-12-30 17:09:41 -06:00
|
|
|
while p.token != token::EOF &&
|
|
|
|
p.token != token::COLON &&
|
|
|
|
p.token != token::MOD_SEP {
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-03-12 02:01:09 -05:00
|
|
|
if clobs.len() != 0 {
|
|
|
|
p.eat(&token::COMMA);
|
|
|
|
}
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-10-07 19:49:10 -05:00
|
|
|
let (s, _str_style) = p.parse_str();
|
|
|
|
let clob = format!("~\\{{}\\}", s);
|
2013-03-12 02:01:09 -05:00
|
|
|
clobs.push(clob);
|
|
|
|
}
|
|
|
|
|
2013-06-10 08:25:25 -05:00
|
|
|
cons = clobs.connect(",");
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
Options => {
|
2013-10-07 19:49:10 -05:00
|
|
|
let (option, _str_style) = p.parse_str();
|
2013-03-12 02:09:53 -05:00
|
|
|
|
2013-06-12 12:02:55 -05:00
|
|
|
if "volatile" == option {
|
2013-03-12 02:01:09 -05:00
|
|
|
volatile = true;
|
2013-06-12 12:02:55 -05:00
|
|
|
} else if "alignstack" == option {
|
2013-03-12 03:02:58 -05:00
|
|
|
alignstack = true;
|
2013-06-12 12:02:55 -05:00
|
|
|
} else if "intel" == option {
|
2013-03-27 16:42:19 -05:00
|
|
|
dialect = ast::asm_intel;
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
|
|
|
|
2013-12-30 17:09:41 -06:00
|
|
|
if p.token == token::COMMA {
|
2013-03-12 02:01:09 -05:00
|
|
|
p.eat(&token::COMMA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-30 17:09:41 -06:00
|
|
|
while p.token == token::COLON ||
|
|
|
|
p.token == token::MOD_SEP ||
|
|
|
|
p.token == token::EOF {
|
|
|
|
state = if p.token == token::COLON {
|
2013-03-12 02:01:09 -05:00
|
|
|
p.bump();
|
|
|
|
match next_state(state) {
|
|
|
|
Some(x) => x,
|
2013-04-26 18:19:26 -05:00
|
|
|
None => {
|
2013-09-25 19:56:54 -05:00
|
|
|
continue_ = false;
|
2013-04-26 18:19:26 -05:00
|
|
|
break
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2013-12-30 17:09:41 -06:00
|
|
|
} else if p.token == token::MOD_SEP {
|
2013-03-12 02:01:09 -05:00
|
|
|
p.bump();
|
|
|
|
let s = match next_state(state) {
|
|
|
|
Some(x) => x,
|
2013-04-26 18:19:26 -05:00
|
|
|
None => {
|
2013-09-25 19:56:54 -05:00
|
|
|
continue_ = false;
|
2013-04-26 18:19:26 -05:00
|
|
|
break
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
};
|
|
|
|
match next_state(s) {
|
|
|
|
Some(x) => x,
|
2013-04-26 18:19:26 -05:00
|
|
|
None => {
|
2013-09-25 19:56:54 -05:00
|
|
|
continue_ = false;
|
2013-04-26 18:19:26 -05:00
|
|
|
break
|
|
|
|
}
|
2013-03-12 02:01:09 -05:00
|
|
|
}
|
2013-12-30 17:09:41 -06:00
|
|
|
} else if p.token == token::EOF {
|
2013-09-25 19:56:54 -05:00
|
|
|
continue_ = false;
|
2013-04-26 18:19:26 -05:00
|
|
|
break;
|
2013-03-12 02:01:09 -05:00
|
|
|
} else {
|
2013-03-12 02:09:53 -05:00
|
|
|
state
|
2013-03-12 02:01:09 -05:00
|
|
|
};
|
|
|
|
}
|
2013-03-11 00:08:38 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 20:45:37 -05:00
|
|
|
MRExpr(@ast::Expr {
|
2013-09-06 21:11:55 -05:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
2013-09-01 20:45:37 -05:00
|
|
|
node: ast::ExprInlineAsm(ast::inline_asm {
|
2013-06-12 12:02:55 -05:00
|
|
|
asm: asm,
|
2013-10-07 19:49:10 -05:00
|
|
|
asm_str_style: asm_str_style.unwrap(),
|
2013-06-12 12:02:55 -05:00
|
|
|
clobbers: cons.to_managed(),
|
2013-03-27 15:42:21 -05:00
|
|
|
inputs: inputs,
|
|
|
|
outputs: outputs,
|
|
|
|
volatile: volatile,
|
2013-03-27 16:42:19 -05:00
|
|
|
alignstack: alignstack,
|
|
|
|
dialect: dialect
|
2013-03-27 15:42:21 -05:00
|
|
|
}),
|
2013-03-11 00:08:38 -05:00
|
|
|
span: sp
|
|
|
|
})
|
|
|
|
}
|