Skip to content

Commit 98fa0f8

Browse files
committed
auto merge of #12798 : pczarn/rust/inline-asm, r=alexcrichton
## read+write modifier '+' This small sugar was left out in the original implementation (#5359). When an output operand with the '+' modifier is encountered, we store the index of that operand alongside the expression to create and append an input operand later. The following lines are equivalent: ``` asm!("" : "+m"(expr)); asm!("" : "=m"(expr) : "0"(expr)); ``` ## misplaced options and clobbers give a warning It's really annoying when a small typo might change behavior without any warning. ``` asm!("mov $1, $0" : "=r"(x) : "r"(8u) : "cc" , "volatile"); //~^ WARNING expected a clobber, but found an option ``` ## liveness Fixed incorrect order of propagation. Sometimes it caused spurious warnings in code: `warning: value assigned to `i` is never read, #[warn(dead_assignment)] on by default` ~~Note: Rebased on top of another PR. (uses other changes)~~ * [x] Implement read+write * [x] Warn about misplaced options * [x] Fix liveness (`dead_assignment` lint) * [x] Add all tests
2 parents b4d3243 + 2a1bd2f commit 98fa0f8

File tree

4 files changed

+182
-54
lines changed

4 files changed

+182
-54
lines changed

src/librustc/middle/liveness.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1259,14 +1259,15 @@ impl Liveness {
12591259
}
12601260

12611261
ExprInlineAsm(ref ia) => {
1262-
let succ = ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
1263-
self.propagate_through_expr(expr, succ)
1264-
});
1265-
ia.outputs.rev_iter().fold(succ, |succ, &(_, expr)| {
1262+
let succ = ia.outputs.rev_iter().fold(succ, |succ, &(_, expr)| {
12661263
// see comment on lvalues in
12671264
// propagate_through_lvalue_components()
12681265
let succ = self.write_lvalue(expr, succ, ACC_WRITE);
12691266
self.propagate_through_lvalue_components(expr, succ)
1267+
});
1268+
// Inputs are executed first. Propagate last because of rev order
1269+
ia.inputs.rev_iter().fold(succ, |succ, &(_, expr)| {
1270+
self.propagate_through_expr(expr, succ)
12701271
})
12711272
}
12721273

src/libsyntax/ext/asm.rs

+69-50
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,25 @@ enum State {
2727
Outputs,
2828
Inputs,
2929
Clobbers,
30-
Options
30+
Options,
31+
StateNone
3132
}
3233

33-
fn next_state(s: State) -> Option<State> {
34-
match s {
35-
Asm => Some(Outputs),
36-
Outputs => Some(Inputs),
37-
Inputs => Some(Clobbers),
38-
Clobbers => Some(Options),
39-
Options => None
34+
impl State {
35+
fn next(&self) -> State {
36+
match *self {
37+
Asm => Outputs,
38+
Outputs => Inputs,
39+
Inputs => Clobbers,
40+
Clobbers => Options,
41+
Options => StateNone,
42+
StateNone => StateNone
43+
}
4044
}
4145
}
4246

47+
static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
48+
4349
pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
4450
-> base::MacResult {
4551
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])
5965

6066
let mut state = Asm;
6167

62-
// Not using labeled break to get us through one round of bootstrapping.
63-
let mut continue_ = true;
64-
while continue_ {
68+
let mut read_write_operands = Vec::new();
69+
70+
'statement: loop {
6571
match state {
6672
Asm => {
6773
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])
8490

8591
let (constraint, _str_style) = p.parse_str();
8692

87-
if constraint.get().starts_with("+") {
88-
cx.span_unimpl(p.last_span,
89-
"'+' (read+write) output operand constraint modifier");
90-
} else if !constraint.get().starts_with("=") {
91-
cx.span_err(p.last_span, "output operand constraint lacks '='");
92-
}
93+
let span = p.last_span;
9394

9495
p.expect(&token::LPAREN);
9596
let out = p.parse_expr();
9697
p.expect(&token::RPAREN);
9798

98-
outputs.push((constraint, out));
99+
// Expands a read+write operand into two operands.
100+
//
101+
// Use '+' modifier when you want the same expression
102+
// to be both an input and an output at the same time.
103+
// It's the opposite of '=&' which means that the memory
104+
// cannot be shared with any other operand (usually when
105+
// a register is clobbered early.)
106+
let output = match constraint.get().slice_shift_char() {
107+
(Some('='), _) => None,
108+
(Some('+'), operand) => {
109+
// Save a reference to the output
110+
read_write_operands.push((outputs.len(), out));
111+
Some(token::intern_and_get_ident("=" + operand))
112+
}
113+
_ => {
114+
cx.span_err(span, "output operand constraint lacks '=' or '+'");
115+
None
116+
}
117+
};
118+
119+
outputs.push((output.unwrap_or(constraint), out));
99120
}
100121
}
101122
Inputs => {
@@ -135,6 +156,10 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
135156
let (s, _str_style) = p.parse_str();
136157
let clob = format!("~\\{{}\\}", s);
137158
clobs.push(clob);
159+
160+
if OPTIONS.iter().any(|opt| s.equiv(opt)) {
161+
cx.span_warn(p.last_span, "expected a clobber, but found an option");
162+
}
138163
}
139164

140165
cons = clobs.connect(",");
@@ -143,56 +168,50 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
143168
let (option, _str_style) = p.parse_str();
144169

145170
if option.equiv(&("volatile")) {
171+
// Indicates that the inline assembly has side effects
172+
// and must not be optimized out along with its outputs.
146173
volatile = true;
147174
} else if option.equiv(&("alignstack")) {
148175
alignstack = true;
149176
} else if option.equiv(&("intel")) {
150177
dialect = ast::AsmIntel;
178+
} else {
179+
cx.span_warn(p.last_span, "unrecognized option");
151180
}
152181

153182
if p.token == token::COMMA {
154183
p.eat(&token::COMMA);
155184
}
156185
}
186+
StateNone => ()
157187
}
158188

159-
while p.token == token::COLON ||
160-
p.token == token::MOD_SEP ||
161-
p.token == token::EOF {
162-
state = if p.token == token::COLON {
163-
p.bump();
164-
match next_state(state) {
165-
Some(x) => x,
166-
None => {
167-
continue_ = false;
168-
break
169-
}
189+
loop {
190+
// MOD_SEP is a double colon '::' without space in between.
191+
// When encountered, the state must be advanced twice.
192+
match (&p.token, state.next(), state.next().next()) {
193+
(&token::COLON, StateNone, _) |
194+
(&token::MOD_SEP, _, StateNone) => {
195+
p.bump();
196+
break 'statement;
170197
}
171-
} else if p.token == token::MOD_SEP {
172-
p.bump();
173-
let s = match next_state(state) {
174-
Some(x) => x,
175-
None => {
176-
continue_ = false;
177-
break
178-
}
179-
};
180-
match next_state(s) {
181-
Some(x) => x,
182-
None => {
183-
continue_ = false;
184-
break
185-
}
198+
(&token::COLON, st, _) |
199+
(&token::MOD_SEP, _, st) => {
200+
p.bump();
201+
state = st;
186202
}
187-
} else if p.token == token::EOF {
188-
continue_ = false;
189-
break;
190-
} else {
191-
state
192-
};
203+
(&token::EOF, _, _) => break 'statement,
204+
_ => break
205+
}
193206
}
194207
}
195208

209+
// Append an input operand, with the form of ("0", expr)
210+
// that links to an output operand.
211+
for &(i, out) in read_write_operands.iter() {
212+
inputs.push((token::intern_and_get_ident(i.to_str()), out));
213+
}
214+
196215
MRExpr(@ast::Expr {
197216
id: ast::DUMMY_NODE_ID,
198217
node: ast::ExprInlineAsm(ast::InlineAsm {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-fast #[feature] doesn't work with check-fast
12+
#[feature(asm)];
13+
14+
#[allow(dead_code)];
15+
16+
#[cfg(target_arch = "x86")]
17+
#[cfg(target_arch = "x86_64")]
18+
pub fn main() {
19+
// assignment not dead
20+
let mut x: int = 0;
21+
unsafe {
22+
// extra colon
23+
asm!("mov $1, $0" : "=r"(x) : "r"(5u), "0"(x) : : "cc");
24+
//~^ WARNING unrecognized option
25+
}
26+
assert_eq!(x, 5);
27+
28+
unsafe {
29+
// comma in place of a colon
30+
asm!("add $2, $1; mov $1, $0" : "=r"(x) : "r"(x), "r"(8u) : "cc", "volatile");
31+
//~^ WARNING expected a clobber, but found an option
32+
}
33+
assert_eq!(x, 13);
34+
}
35+
36+
// #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
37+
// pub fn main() {}
38+
39+
// At least one error is needed so that compilation fails
40+
#[static_assert]
41+
static b: bool = false; //~ ERROR static assertion failed
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-fast #[feature] doesn't work with check-fast
12+
#[feature(asm)];
13+
14+
#[cfg(target_arch = "x86")]
15+
#[cfg(target_arch = "x86_64")]
16+
unsafe fn next_power_of_2(n: u32) -> u32 {
17+
let mut tmp = n;
18+
asm!("dec $0" : "+rm"(tmp) :: "cc");
19+
let mut shift = 1u;
20+
while shift <= 16 {
21+
asm!(
22+
"shr %cl, $2
23+
or $2, $0
24+
shl $$1, $1"
25+
: "+&rm"(tmp), "+{ecx}"(shift) : "r"(tmp) : "cc"
26+
);
27+
}
28+
asm!("inc $0" : "+rm"(tmp) :: "cc");
29+
return tmp;
30+
}
31+
32+
#[cfg(target_arch = "x86")]
33+
#[cfg(target_arch = "x86_64")]
34+
pub fn main() {
35+
unsafe {
36+
assert_eq!(64, next_power_of_2(37));
37+
assert_eq!(2147483648, next_power_of_2(2147483647));
38+
}
39+
40+
let mut y: int = 5;
41+
let x: int;
42+
unsafe {
43+
// Treat the output as initialization.
44+
asm!(
45+
"shl $2, $1
46+
add $3, $1
47+
mov $1, $0"
48+
: "=r"(x), "+r"(y) : "i"(3u), "ir"(7u) : "cc"
49+
);
50+
}
51+
assert_eq!(x, 47);
52+
assert_eq!(y, 47);
53+
54+
let mut x = x + 1;
55+
assert_eq!(x, 48);
56+
57+
unsafe {
58+
// Assignment to mutable.
59+
// Early clobber "&":
60+
// Forbids the use of a single register by both operands.
61+
asm!("shr $$2, $1; add $1, $0" : "+&r"(x) : "r"(x) : "cc");
62+
}
63+
assert_eq!(x, 60);
64+
}
65+
66+
#[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
67+
pub fn main() {}

0 commit comments

Comments
 (0)