@@ -27,19 +27,25 @@ enum State {
27
27
Outputs ,
28
28
Inputs ,
29
29
Clobbers ,
30
- Options
30
+ Options ,
31
+ StateNone
31
32
}
32
33
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
+ }
40
44
}
41
45
}
42
46
47
+ static OPTIONS : & ' static [ & ' static str ] = & [ "volatile" , "alignstack" , "intel" ] ;
48
+
43
49
pub fn expand_asm ( cx : & mut ExtCtxt , sp : Span , tts : & [ ast:: TokenTree ] )
44
50
-> base:: MacResult {
45
51
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])
59
65
60
66
let mut state = Asm ;
61
67
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 {
65
71
match state {
66
72
Asm => {
67
73
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])
84
90
85
91
let ( constraint, _str_style) = p. parse_str ( ) ;
86
92
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 ;
93
94
94
95
p. expect ( & token:: LPAREN ) ;
95
96
let out = p. parse_expr ( ) ;
96
97
p. expect ( & token:: RPAREN ) ;
97
98
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) ) ;
99
120
}
100
121
}
101
122
Inputs => {
@@ -135,6 +156,10 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
135
156
let ( s, _str_style) = p. parse_str ( ) ;
136
157
let clob = format ! ( "~\\ {{}\\ }" , s) ;
137
158
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
+ }
138
163
}
139
164
140
165
cons = clobs. connect ( "," ) ;
@@ -143,56 +168,50 @@ pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
143
168
let ( option, _str_style) = p. parse_str ( ) ;
144
169
145
170
if option. equiv ( & ( "volatile" ) ) {
171
+ // Indicates that the inline assembly has side effects
172
+ // and must not be optimized out along with its outputs.
146
173
volatile = true ;
147
174
} else if option. equiv ( & ( "alignstack" ) ) {
148
175
alignstack = true ;
149
176
} else if option. equiv ( & ( "intel" ) ) {
150
177
dialect = ast:: AsmIntel ;
178
+ } else {
179
+ cx. span_warn ( p. last_span , "unrecognized option" ) ;
151
180
}
152
181
153
182
if p. token == token:: COMMA {
154
183
p. eat ( & token:: COMMA ) ;
155
184
}
156
185
}
186
+ StateNone => ( )
157
187
}
158
188
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;
170
197
}
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;
186
202
}
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
+ }
193
206
}
194
207
}
195
208
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
+
196
215
MRExpr ( @ast:: Expr {
197
216
id : ast:: DUMMY_NODE_ID ,
198
217
node : ast:: ExprInlineAsm ( ast:: InlineAsm {
0 commit comments