@@ -18,30 +18,30 @@ use crate::type_of::LayoutGccExt;
18
18
19
19
// Rust asm! and GCC Extended Asm semantics differ substantially.
20
20
//
21
- // 1. Rust asm operands go along as one list of operands. Operands themselves indicate
22
- // if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
21
+ // 1. Rust asm operands go along as one list of operands. Operands themselves indicate
22
+ // if they're "in" or "out". "In" and "out" operands can interleave. One operand can be
23
23
// both "in" and "out" (`inout(reg)`).
24
24
//
25
- // GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
26
- // this means that all "out" operands must go before "in" operands. "In" and "out" operands
25
+ // GCC asm has two different lists for "in" and "out" operands. In terms of gccjit,
26
+ // this means that all "out" operands must go before "in" operands. "In" and "out" operands
27
27
// cannot interleave.
28
28
//
29
- // 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
29
+ // 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important
30
30
// because the asm template refers to operands by index.
31
31
//
32
32
// Mapping from Rust to GCC index would be 1-1 if it wasn't for...
33
33
//
34
- // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
35
- // Contrary, Rust expresses clobbers through "out" operands that aren't tied to
34
+ // 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes.
35
+ // Contrary, Rust expresses clobbers through "out" operands that aren't tied to
36
36
// a variable (`_`), and such "clobbers" do have index.
37
37
//
38
- // 4. Furthermore, GCC Extended Asm does not support explicit register constraints
39
- // (like `out("eax")`) directly, offering so-called "local register variables"
40
- // as a workaround. These variables need to be declared and initialized *before*
41
- // the Extended Asm block but *after* normal local variables
38
+ // 4. Furthermore, GCC Extended Asm does not support explicit register constraints
39
+ // (like `out("eax")`) directly, offering so-called "local register variables"
40
+ // as a workaround. These variables need to be declared and initialized *before*
41
+ // the Extended Asm block but *after* normal local variables
42
42
// (see comment in `codegen_inline_asm` for explanation).
43
43
//
44
- // With that in mind, let's see how we translate Rust syntax to GCC
44
+ // With that in mind, let's see how we translate Rust syntax to GCC
45
45
// (from now on, `CC` stands for "constraint code"):
46
46
//
47
47
// * `out(reg_class) var` -> translated to output operand: `"=CC"(var)`
@@ -52,18 +52,17 @@ use crate::type_of::LayoutGccExt;
52
52
//
53
53
// * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list
54
54
//
55
- // * `inout(reg_class) in_var => out_var` -> translated to two operands:
55
+ // * `inout(reg_class) in_var => out_var` -> translated to two operands:
56
56
// output: `"=CC"(in_var)`
57
- // input: `"num"(out_var)` where num is the GCC index
57
+ // input: `"num"(out_var)` where num is the GCC index
58
58
// of the corresponding output operand
59
59
//
60
- // * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
60
+ // * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`,
61
61
// where "tmp" is a temporary unused variable
62
62
//
63
- // * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
64
- // with `"r"(var)` constraint,
63
+ // * `out/in/inout("explicit register") var` -> translated to one or two operands as described above
64
+ // with `"r"(var)` constraint,
65
65
// and one register variable assigned to the desired register.
66
- //
67
66
68
67
const ATT_SYNTAX_INS : & str = ".att_syntax noprefix\n \t " ;
69
68
const INTEL_SYNTAX_INS : & str = "\n \t .intel_syntax noprefix" ;
@@ -124,7 +123,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
124
123
let att_dialect = is_x86 && options. contains ( InlineAsmOptions :: ATT_SYNTAX ) ;
125
124
let intel_dialect = is_x86 && !options. contains ( InlineAsmOptions :: ATT_SYNTAX ) ;
126
125
127
- // GCC index of an output operand equals its position in the array
126
+ // GCC index of an output operand equals its position in the array
128
127
let mut outputs = vec ! [ ] ;
129
128
130
129
// GCC index of an input operand equals its position in the array
@@ -138,9 +137,9 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
138
137
let mut constants_len = 0 ;
139
138
140
139
// There are rules we must adhere to if we want GCC to do the right thing:
141
- //
140
+ //
142
141
// * Every local variable that the asm block uses as an output must be declared *before*
143
- // the asm block.
142
+ // the asm block.
144
143
// * There must be no instructions whatsoever between the register variables and the asm.
145
144
//
146
145
// Therefore, the backend must generate the instructions strictly in this order:
@@ -152,7 +151,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
152
151
// We also must make sure that no input operands are emitted before output operands.
153
152
//
154
153
// This is why we work in passes, first emitting local vars, then local register vars.
155
- // Also, we don't emit any asm operands immediately; we save them to
154
+ // Also, we don't emit any asm operands immediately; we save them to
156
155
// the one of the buffers to be emitted later.
157
156
158
157
// 1. Normal variables (and saving operands to buffers).
@@ -165,7 +164,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
165
164
( Constraint ( constraint) , Some ( place) ) => ( constraint, place. layout . gcc_type ( self . cx , false ) ) ,
166
165
// When `reg` is a class and not an explicit register but the out place is not specified,
167
166
// we need to create an unused output variable to assign the output to. This var
168
- // needs to be of a type that's "compatible" with the register class, but specific type
167
+ // needs to be of a type that's "compatible" with the register class, but specific type
169
168
// doesn't matter.
170
169
( Constraint ( constraint) , None ) => ( constraint, dummy_output_type ( self . cx , reg. reg_class ( ) ) ) ,
171
170
( Register ( _) , Some ( _) ) => {
@@ -193,7 +192,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
193
192
194
193
let tmp_var = self . current_func ( ) . new_local ( None , ty, "output_register" ) ;
195
194
outputs. push ( AsmOutOperand {
196
- constraint,
195
+ constraint,
197
196
rust_idx,
198
197
late,
199
198
readwrite : false ,
@@ -204,12 +203,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
204
203
205
204
InlineAsmOperandRef :: In { reg, value } => {
206
205
if let ConstraintOrRegister :: Constraint ( constraint) = reg_to_gcc ( reg) {
207
- inputs. push ( AsmInOperand {
208
- constraint : Cow :: Borrowed ( constraint) ,
209
- rust_idx,
206
+ inputs. push ( AsmInOperand {
207
+ constraint : Cow :: Borrowed ( constraint) ,
208
+ rust_idx,
210
209
val : value. immediate ( )
211
210
} ) ;
212
- }
211
+ }
213
212
else {
214
213
// left for the next pass
215
214
continue
@@ -219,7 +218,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
219
218
InlineAsmOperandRef :: InOut { reg, late, in_value, out_place } => {
220
219
let constraint = if let ConstraintOrRegister :: Constraint ( constraint) = reg_to_gcc ( reg) {
221
220
constraint
222
- }
221
+ }
223
222
else {
224
223
// left for the next pass
225
224
continue
@@ -228,22 +227,22 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
228
227
// Rustc frontend guarantees that input and output types are "compatible",
229
228
// so we can just use input var's type for the output variable.
230
229
//
231
- // This decision is also backed by the fact that LLVM needs in and out
232
- // values to be of *exactly the same type*, not just "compatible".
230
+ // This decision is also backed by the fact that LLVM needs in and out
231
+ // values to be of *exactly the same type*, not just "compatible".
233
232
// I'm not sure if GCC is so picky too, but better safe than sorry.
234
233
let ty = in_value. layout . gcc_type ( self . cx , false ) ;
235
234
let tmp_var = self . current_func ( ) . new_local ( None , ty, "output_register" ) ;
236
235
237
236
// If the out_place is None (i.e `inout(reg) _` syntax was used), we translate
238
- // it to one "readwrite (+) output variable", otherwise we translate it to two
237
+ // it to one "readwrite (+) output variable", otherwise we translate it to two
239
238
// "out and tied in" vars as described above.
240
239
let readwrite = out_place. is_none ( ) ;
241
240
outputs. push ( AsmOutOperand {
242
- constraint,
241
+ constraint,
243
242
rust_idx,
244
243
late,
245
244
readwrite,
246
- tmp_var,
245
+ tmp_var,
247
246
out_place,
248
247
} ) ;
249
248
@@ -252,8 +251,8 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
252
251
let constraint = Cow :: Owned ( out_gcc_idx. to_string ( ) ) ;
253
252
254
253
inputs. push ( AsmInOperand {
255
- constraint,
256
- rust_idx,
254
+ constraint,
255
+ rust_idx,
257
256
val : in_value. immediate ( )
258
257
} ) ;
259
258
}
@@ -280,7 +279,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
280
279
if let ConstraintOrRegister :: Register ( reg_name) = reg_to_gcc ( reg) {
281
280
let out_place = if let Some ( place) = place {
282
281
place
283
- }
282
+ }
284
283
else {
285
284
// processed in the previous pass
286
285
continue
@@ -291,7 +290,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
291
290
tmp_var. set_register_name ( reg_name) ;
292
291
293
292
outputs. push ( AsmOutOperand {
294
- constraint : "r" . into ( ) ,
293
+ constraint : "r" . into ( ) ,
295
294
rust_idx,
296
295
late,
297
296
readwrite : false ,
@@ -311,9 +310,9 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
311
310
reg_var. set_register_name ( reg_name) ;
312
311
self . llbb ( ) . add_assignment ( None , reg_var, value. immediate ( ) ) ;
313
312
314
- inputs. push ( AsmInOperand {
315
- constraint : "r" . into ( ) ,
316
- rust_idx,
313
+ inputs. push ( AsmInOperand {
314
+ constraint : "r" . into ( ) ,
315
+ rust_idx,
317
316
val : reg_var. to_rvalue ( )
318
317
} ) ;
319
318
}
@@ -324,31 +323,23 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
324
323
// `inout("explicit register") in_var => out_var`
325
324
InlineAsmOperandRef :: InOut { reg, late, in_value, out_place } => {
326
325
if let ConstraintOrRegister :: Register ( reg_name) = reg_to_gcc ( reg) {
327
- let out_place = if let Some ( place) = out_place {
328
- place
329
- }
330
- else {
331
- // processed in the previous pass
332
- continue
333
- } ;
334
-
335
326
// See explanation in the first pass.
336
327
let ty = in_value. layout . gcc_type ( self . cx , false ) ;
337
328
let tmp_var = self . current_func ( ) . new_local ( None , ty, "output_register" ) ;
338
329
tmp_var. set_register_name ( reg_name) ;
339
330
340
331
outputs. push ( AsmOutOperand {
341
- constraint : "r" . into ( ) ,
332
+ constraint : "r" . into ( ) ,
342
333
rust_idx,
343
334
late,
344
335
readwrite : false ,
345
336
tmp_var,
346
- out_place : Some ( out_place )
337
+ out_place,
347
338
} ) ;
348
339
349
340
let constraint = Cow :: Owned ( ( outputs. len ( ) - 1 ) . to_string ( ) ) ;
350
- inputs. push ( AsmInOperand {
351
- constraint,
341
+ inputs. push ( AsmInOperand {
342
+ constraint,
352
343
rust_idx,
353
344
val : in_value. immediate ( )
354
345
} ) ;
@@ -357,8 +348,8 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
357
348
// processed in the previous pass
358
349
}
359
350
360
- InlineAsmOperandRef :: Const { .. }
361
- | InlineAsmOperandRef :: SymFn { .. }
351
+ InlineAsmOperandRef :: Const { .. }
352
+ | InlineAsmOperandRef :: SymFn { .. }
362
353
| InlineAsmOperandRef :: SymStatic { .. } => {
363
354
// processed in the previous pass
364
355
}
@@ -453,7 +444,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
453
444
if !intel_dialect {
454
445
template_str. push_str ( INTEL_SYNTAX_INS ) ;
455
446
}
456
-
447
+
457
448
// 4. Generate Extended Asm block
458
449
459
450
let block = self . llbb ( ) ;
@@ -472,7 +463,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
472
463
}
473
464
474
465
if !options. contains ( InlineAsmOptions :: PRESERVES_FLAGS ) {
475
- // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
466
+ // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient
476
467
// on all architectures. For instance, what about FP stack?
477
468
extended_asm. add_clobber ( "cc" ) ;
478
469
}
@@ -491,18 +482,18 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
491
482
self . call ( self . type_void ( ) , builtin_unreachable, & [ ] , None ) ;
492
483
}
493
484
494
- // Write results to outputs.
485
+ // Write results to outputs.
495
486
//
496
487
// We need to do this because:
497
- // 1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
488
+ // 1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases
498
489
// (especially with current `rustc_backend_ssa` API).
499
490
// 2. Not every output operand has an `out_place`, and it's required by `add_output_operand`.
500
491
//
501
492
// Instead, we generate a temporary output variable for each output operand, and then this loop,
502
493
// generates `out_place = tmp_var;` assignments if out_place exists.
503
494
for op in & outputs {
504
495
if let Some ( place) = op. out_place {
505
- OperandValue :: Immediate ( op. tmp_var . to_rvalue ( ) ) . store ( self , place) ;
496
+ OperandValue :: Immediate ( op. tmp_var . to_rvalue ( ) ) . store ( self , place) ;
506
497
}
507
498
}
508
499
0 commit comments