@@ -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+
4349pub 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 {
0 commit comments