Fix and improve inline assembly.
Read+write modifier Some documentation in asm.rs rpass and cfail tests
This commit is contained in:
parent
3316a0e6b2
commit
2a1bd2ff9f
@ -1259,14 +1259,15 @@ impl Liveness {
|
||||
}
|
||||
|
||||
ExprInlineAsm(ref ia) => {
|
||||
let succ = ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
|
||||
self.propagate_through_expr(expr, succ)
|
||||
});
|
||||
ia.outputs.rev_iter().fold(succ, |succ, &(_, expr)| {
|
||||
let succ = ia.outputs.rev_iter().fold(succ, |succ, &(_, expr)| {
|
||||
// see comment on lvalues in
|
||||
// propagate_through_lvalue_components()
|
||||
let succ = self.write_lvalue(expr, succ, ACC_WRITE);
|
||||
self.propagate_through_lvalue_components(expr, succ)
|
||||
});
|
||||
// Inputs are executed first. Propagate last because of rev order
|
||||
ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
|
||||
self.propagate_through_expr(expr, succ)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -27,19 +27,25 @@ enum State {
|
||||
Outputs,
|
||||
Inputs,
|
||||
Clobbers,
|
||||
Options
|
||||
Options,
|
||||
StateNone
|
||||
}
|
||||
|
||||
fn next_state(s: State) -> Option<State> {
|
||||
match s {
|
||||
Asm => Some(Outputs),
|
||||
Outputs => Some(Inputs),
|
||||
Inputs => Some(Clobbers),
|
||||
Clobbers => Some(Options),
|
||||
Options => None
|
||||
impl State {
|
||||
fn next(&self) -> State {
|
||||
match *self {
|
||||
Asm => Outputs,
|
||||
Outputs => Inputs,
|
||||
Inputs => Clobbers,
|
||||
Clobbers => Options,
|
||||
Options => StateNone,
|
||||
StateNone => StateNone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
|
||||
|
||||
pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> base::MacResult {
|
||||
let mut p = parse::new_parser_from_tts(cx.parse_sess(),
|
||||
@ -59,9 +65,9 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
|
||||
let mut state = Asm;
|
||||
|
||||
// Not using labeled break to get us through one round of bootstrapping.
|
||||
let mut continue_ = true;
|
||||
while continue_ {
|
||||
let mut read_write_operands = Vec::new();
|
||||
|
||||
'statement: loop {
|
||||
match state {
|
||||
Asm => {
|
||||
let (s, style) = match expr_to_str(cx, p.parse_expr(),
|
||||
@ -84,18 +90,33 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
|
||||
let (constraint, _str_style) = p.parse_str();
|
||||
|
||||
if constraint.get().starts_with("+") {
|
||||
cx.span_unimpl(p.last_span,
|
||||
"'+' (read+write) output operand constraint modifier");
|
||||
} else if !constraint.get().starts_with("=") {
|
||||
cx.span_err(p.last_span, "output operand constraint lacks '='");
|
||||
}
|
||||
let span = p.last_span;
|
||||
|
||||
p.expect(&token::LPAREN);
|
||||
let out = p.parse_expr();
|
||||
p.expect(&token::RPAREN);
|
||||
|
||||
outputs.push((constraint, out));
|
||||
// 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 output = match constraint.get().slice_shift_char() {
|
||||
(Some('='), _) => None,
|
||||
(Some('+'), operand) => {
|
||||
// Save a reference to the output
|
||||
read_write_operands.push((outputs.len(), out));
|
||||
Some(token::intern_and_get_ident("=" + operand))
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(span, "output operand constraint lacks '=' or '+'");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
outputs.push((output.unwrap_or(constraint), out));
|
||||
}
|
||||
}
|
||||
Inputs => {
|
||||
@ -135,6 +156,10 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
let (s, _str_style) = p.parse_str();
|
||||
let clob = format!("~\\{{}\\}", s);
|
||||
clobs.push(clob);
|
||||
|
||||
if OPTIONS.iter().any(|opt| s.equiv(opt)) {
|
||||
cx.span_warn(p.last_span, "expected a clobber, but found an option");
|
||||
}
|
||||
}
|
||||
|
||||
cons = clobs.connect(",");
|
||||
@ -143,56 +168,50 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
let (option, _str_style) = p.parse_str();
|
||||
|
||||
if option.equiv(&("volatile")) {
|
||||
// Indicates that the inline assembly has side effects
|
||||
// and must not be optimized out along with its outputs.
|
||||
volatile = true;
|
||||
} else if option.equiv(&("alignstack")) {
|
||||
alignstack = true;
|
||||
} else if option.equiv(&("intel")) {
|
||||
dialect = ast::AsmIntel;
|
||||
} else {
|
||||
cx.span_warn(p.last_span, "unrecognized option");
|
||||
}
|
||||
|
||||
if p.token == token::COMMA {
|
||||
p.eat(&token::COMMA);
|
||||
}
|
||||
}
|
||||
StateNone => ()
|
||||
}
|
||||
|
||||
while p.token == token::COLON ||
|
||||
p.token == token::MOD_SEP ||
|
||||
p.token == token::EOF {
|
||||
state = if p.token == token::COLON {
|
||||
p.bump();
|
||||
match next_state(state) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
continue_ = false;
|
||||
break
|
||||
}
|
||||
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()) {
|
||||
(&token::COLON, StateNone, _) |
|
||||
(&token::MOD_SEP, _, StateNone) => {
|
||||
p.bump();
|
||||
break 'statement;
|
||||
}
|
||||
} else if p.token == token::MOD_SEP {
|
||||
p.bump();
|
||||
let s = match next_state(state) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
continue_ = false;
|
||||
break
|
||||
}
|
||||
};
|
||||
match next_state(s) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
continue_ = false;
|
||||
break
|
||||
}
|
||||
(&token::COLON, st, _) |
|
||||
(&token::MOD_SEP, _, st) => {
|
||||
p.bump();
|
||||
state = st;
|
||||
}
|
||||
} else if p.token == token::EOF {
|
||||
continue_ = false;
|
||||
break;
|
||||
} else {
|
||||
state
|
||||
};
|
||||
(&token::EOF, _, _) => break 'statement,
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append an input operand, with the form of ("0", expr)
|
||||
// that links to an output operand.
|
||||
for &(i, out) in read_write_operands.iter() {
|
||||
inputs.push((token::intern_and_get_ident(i.to_str()), out));
|
||||
}
|
||||
|
||||
MRExpr(@ast::Expr {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprInlineAsm(ast::InlineAsm {
|
||||
|
41
src/test/compile-fail/asm-misplaced-option.rs
Normal file
41
src/test/compile-fail/asm-misplaced-option.rs
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// 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.
|
||||
|
||||
// ignore-fast #[feature] doesn't work with check-fast
|
||||
#[feature(asm)];
|
||||
|
||||
#[allow(dead_code)];
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn main() {
|
||||
// assignment not dead
|
||||
let mut x: int = 0;
|
||||
unsafe {
|
||||
// extra colon
|
||||
asm!("mov $1, $0" : "=r"(x) : "r"(5u), "0"(x) : : "cc");
|
||||
//~^ WARNING unrecognized option
|
||||
}
|
||||
assert_eq!(x, 5);
|
||||
|
||||
unsafe {
|
||||
// comma in place of a colon
|
||||
asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile");
|
||||
//~^ WARNING expected a clobber, but found an option
|
||||
}
|
||||
assert_eq!(x, 13);
|
||||
}
|
||||
|
||||
// #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
|
||||
// pub fn main() {}
|
||||
|
||||
// At least one error is needed so that compilation fails
|
||||
#[static_assert]
|
||||
static b: bool = false; //~ ERROR static assertion failed
|
67
src/test/run-pass/asm-in-out-operand.rs
Normal file
67
src/test/run-pass/asm-in-out-operand.rs
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// 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.
|
||||
|
||||
// ignore-fast #[feature] doesn't work with check-fast
|
||||
#[feature(asm)];
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe fn next_power_of_2(n: u32) -> u32 {
|
||||
let mut tmp = n;
|
||||
asm!("dec $0" : "+rm"(tmp) :: "cc");
|
||||
let mut shift = 1u;
|
||||
while shift <= 16 {
|
||||
asm!(
|
||||
"shr %cl, $2
|
||||
or $2, $0
|
||||
shl $$1, $1"
|
||||
: "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc"
|
||||
);
|
||||
}
|
||||
asm!("inc $0" : "+rm"(tmp) :: "cc");
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub fn main() {
|
||||
unsafe {
|
||||
assert_eq!(64, next_power_of_2(37));
|
||||
assert_eq!(2147483648, next_power_of_2(2147483647));
|
||||
}
|
||||
|
||||
let mut y: int = 5;
|
||||
let x: int;
|
||||
unsafe {
|
||||
// Treat the output as initialization.
|
||||
asm!(
|
||||
"shl $2, $1
|
||||
add $3, $1
|
||||
mov $1, $0"
|
||||
: "=r"(x), "+r"(y) : "i"(3u), "ir"(7u) : "cc"
|
||||
);
|
||||
}
|
||||
assert_eq!(x, 47);
|
||||
assert_eq!(y, 47);
|
||||
|
||||
let mut x = x + 1;
|
||||
assert_eq!(x, 48);
|
||||
|
||||
unsafe {
|
||||
// Assignment to mutable.
|
||||
// Early clobber "&":
|
||||
// Forbids the use of a single register by both operands.
|
||||
asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc");
|
||||
}
|
||||
assert_eq!(x, 60);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
|
||||
pub fn main() {}
|
Loading…
x
Reference in New Issue
Block a user