@@ -18,9 +18,11 @@ use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
18
18
use rustc_codegen_ssa:: mir:: operand:: OperandValue ;
19
19
use rustc_codegen_ssa:: mir:: place:: PlaceRef ;
20
20
use rustc_codegen_ssa:: traits:: {
21
- AsmBuilderMethods , BackendTypes , BuilderMethods , InlineAsmOperandRef ,
21
+ AsmBuilderMethods , AsmCodegenMethods , BackendTypes , BuilderMethods , GlobalAsmOperandRef ,
22
+ InlineAsmOperandRef ,
22
23
} ;
23
24
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
25
+ use rustc_errors:: { Diag , DiagMessage } ;
24
26
use rustc_middle:: { bug, ty:: Instance } ;
25
27
use rustc_span:: { DUMMY_SP , Span } ;
26
28
use rustc_target:: asm:: { InlineAsmRegClass , InlineAsmRegOrRegClass , SpirVInlineAsmRegClass } ;
@@ -68,6 +70,153 @@ fn inline_asm_operand_ref_clone<'tcx, B: BackendTypes + ?Sized>(
68
70
}
69
71
}
70
72
73
+ impl < ' tcx > AsmCodegenMethods < ' tcx > for CodegenCx < ' tcx > {
74
+ fn codegen_global_asm (
75
+ & mut self ,
76
+ template : & [ InlineAsmTemplatePiece ] ,
77
+ operands : & [ GlobalAsmOperandRef < ' tcx > ] ,
78
+ options : InlineAsmOptions ,
79
+ line_spans : & [ Span ] ,
80
+ ) {
81
+ const SUPPORTED_OPTIONS : InlineAsmOptions = InlineAsmOptions :: empty ( ) ;
82
+ let unsupported_options = options & !SUPPORTED_OPTIONS ;
83
+ if !unsupported_options. is_empty ( ) {
84
+ self . tcx . dcx ( ) . span_err (
85
+ line_spans. first ( ) . copied ( ) . unwrap_or_default ( ) ,
86
+ format ! ( "global_asm! flags not supported: {unsupported_options:?}" ) ,
87
+ ) ;
88
+ }
89
+
90
+ // vec of lines, and each line is vec of tokens
91
+ let mut tokens = vec ! [ vec![ ] ] ;
92
+ for piece in template {
93
+ match piece {
94
+ InlineAsmTemplatePiece :: String ( asm) => {
95
+ // We cannot use str::lines() here because we don't want the behavior of "the
96
+ // last newline is optional", we want an empty string for the last line if
97
+ // there is no newline terminator.
98
+ // Lambda copied from std LinesAnyMap
99
+ let lines = asm. split ( '\n' ) . map ( |line| {
100
+ let l = line. len ( ) ;
101
+ if l > 0 && line. as_bytes ( ) [ l - 1 ] == b'\r' {
102
+ & line[ 0 ..l - 1 ]
103
+ } else {
104
+ line
105
+ }
106
+ } ) ;
107
+ for ( index, line) in lines. enumerate ( ) {
108
+ if index != 0 {
109
+ // There was a newline, add a new line.
110
+ tokens. push ( vec ! [ ] ) ;
111
+ }
112
+ let mut chars = line. chars ( ) ;
113
+
114
+ let span = line_spans
115
+ . get ( tokens. len ( ) - 1 )
116
+ . copied ( )
117
+ . unwrap_or_default ( ) ;
118
+ while let Some ( token) = InlineAsmCx :: Global ( self , span) . lex_word ( & mut chars)
119
+ {
120
+ tokens. last_mut ( ) . unwrap ( ) . push ( token) ;
121
+ }
122
+ }
123
+ }
124
+ & InlineAsmTemplatePiece :: Placeholder {
125
+ operand_idx,
126
+ modifier,
127
+ span,
128
+ } => {
129
+ if let Some ( modifier) = modifier {
130
+ self . tcx
131
+ . dcx ( )
132
+ . span_err ( span, format ! ( "asm modifiers are not supported: {modifier}" ) ) ;
133
+ }
134
+ let span = line_spans
135
+ . get ( tokens. len ( ) - 1 )
136
+ . copied ( )
137
+ . unwrap_or_default ( ) ;
138
+ let line = tokens. last_mut ( ) . unwrap ( ) ;
139
+ let typeof_kind = line. last ( ) . and_then ( |prev| match prev {
140
+ Token :: Word ( "typeof" ) => Some ( TypeofKind :: Plain ) ,
141
+ Token :: Word ( "typeof*" ) => Some ( TypeofKind :: Dereference ) ,
142
+ _ => None ,
143
+ } ) ;
144
+ let operand = & operands[ operand_idx] ;
145
+ match typeof_kind {
146
+ Some ( _) => match operand {
147
+ GlobalAsmOperandRef :: Const { string : _ } => {
148
+ self . tcx
149
+ . dcx ( )
150
+ . span_err ( span, "cannot take the type of a const asm argument" ) ;
151
+ }
152
+ GlobalAsmOperandRef :: SymFn { instance : _ } => {
153
+ self . tcx . dcx ( ) . span_err (
154
+ span,
155
+ "cannot take the type of a function asm argument" ,
156
+ ) ;
157
+ }
158
+ GlobalAsmOperandRef :: SymStatic { def_id : _ } => {
159
+ self . tcx . dcx ( ) . span_err (
160
+ span,
161
+ "cannot take the type of a static variable asm argument" ,
162
+ ) ;
163
+ }
164
+ } ,
165
+ None => match operand {
166
+ GlobalAsmOperandRef :: Const { string } => line. push ( Token :: Word ( string) ) ,
167
+ GlobalAsmOperandRef :: SymFn { instance : _ } => {
168
+ self . tcx
169
+ . dcx ( )
170
+ . span_err ( span, "function asm argument not supported yet" ) ;
171
+ }
172
+ GlobalAsmOperandRef :: SymStatic { def_id : _ } => {
173
+ self . tcx . dcx ( ) . span_err (
174
+ span,
175
+ "static variable asm argument not supported yet" ,
176
+ ) ;
177
+ }
178
+ } ,
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ let mut id_map = FxHashMap :: default ( ) ;
185
+ let mut defined_ids = FxHashSet :: default ( ) ;
186
+ let mut id_to_type_map = FxHashMap :: default ( ) ;
187
+
188
+ let mut asm_block = AsmBlock :: Open ;
189
+ for ( line_idx, line) in tokens. into_iter ( ) . enumerate ( ) {
190
+ let span = line_spans. get ( line_idx) . copied ( ) . unwrap_or_default ( ) ;
191
+ InlineAsmCx :: Global ( self , span) . codegen_asm (
192
+ & mut id_map,
193
+ & mut defined_ids,
194
+ & mut id_to_type_map,
195
+ & mut asm_block,
196
+ line. into_iter ( ) ,
197
+ ) ;
198
+ }
199
+
200
+ for ( id, num) in id_map {
201
+ if !defined_ids. contains ( & num) {
202
+ self . tcx . dcx ( ) . span_err (
203
+ line_spans. first ( ) . copied ( ) . unwrap_or_default ( ) ,
204
+ format ! ( "%{id} is used but not defined" ) ,
205
+ ) ;
206
+ }
207
+ }
208
+ }
209
+
210
+ // FIXME(eddyb) should this method be implemented as just symbol mangling,
211
+ // or renamed upstream into something much more specific?
212
+ fn mangled_name ( & self , instance : Instance < ' tcx > ) -> String {
213
+ self . tcx . dcx ( ) . span_bug (
214
+ self . tcx . def_span ( instance. def_id ( ) ) ,
215
+ "[Rust-GPU] `#[naked] fn` not yet supported" ,
216
+ )
217
+ }
218
+ }
219
+
71
220
impl < ' a , ' tcx > AsmBuilderMethods < ' tcx > for Builder < ' a , ' tcx > {
72
221
/* Example asm and the template it compiles to:
73
222
asm!(
@@ -103,7 +252,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
103
252
const SUPPORTED_OPTIONS : InlineAsmOptions = InlineAsmOptions :: NORETURN ;
104
253
let unsupported_options = options & !SUPPORTED_OPTIONS ;
105
254
if !unsupported_options. is_empty ( ) {
106
- self . err ( format ! ( "asm flags not supported: {unsupported_options:?}" ) ) ;
255
+ self . err ( format ! ( "asm! flags not supported: {unsupported_options:?}" ) ) ;
107
256
}
108
257
109
258
// HACK(eddyb) get more accurate pointers types, for pointer operands,
@@ -165,7 +314,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
165
314
tokens. push ( vec ! [ ] ) ;
166
315
}
167
316
let mut chars = line. chars ( ) ;
168
- while let Some ( token) = self . lex_word ( & mut chars) {
317
+ while let Some ( token) = InlineAsmCx :: Local ( self ) . lex_word ( & mut chars) {
169
318
tokens. last_mut ( ) . unwrap ( ) . push ( token) ;
170
319
}
171
320
}
@@ -222,7 +371,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
222
371
// which is an unfortunate interaction, but perhaps avoidable?
223
372
AsmBlock :: End ( _) => { }
224
373
}
225
- self . codegen_asm (
374
+ InlineAsmCx :: Local ( self ) . codegen_asm (
226
375
& mut id_map,
227
376
& mut defined_ids,
228
377
& mut id_to_type_map,
@@ -287,7 +436,47 @@ enum AsmBlock {
287
436
End ( Op ) ,
288
437
}
289
438
290
- impl < ' cx , ' tcx > Builder < ' cx , ' tcx > {
439
+ enum InlineAsmCx < ' a , ' cx , ' tcx > {
440
+ Global ( & ' cx CodegenCx < ' tcx > , Span ) ,
441
+ Local ( & ' a mut Builder < ' cx , ' tcx > ) ,
442
+ }
443
+
444
+ impl < ' cx , ' tcx > std:: ops:: Deref for InlineAsmCx < ' _ , ' cx , ' tcx > {
445
+ type Target = & ' cx CodegenCx < ' tcx > ;
446
+ fn deref ( & self ) -> & Self :: Target {
447
+ match self {
448
+ Self :: Global ( cx, _) | Self :: Local ( Builder { cx, .. } ) => cx,
449
+ }
450
+ }
451
+ }
452
+
453
+ impl InlineAsmCx < ' _ , ' _ , ' _ > {
454
+ fn span ( & self ) -> Span {
455
+ match self {
456
+ & Self :: Global ( _, span) => span,
457
+ Self :: Local ( bx) => bx. span ( ) ,
458
+ }
459
+ }
460
+
461
+ #[ track_caller]
462
+ fn struct_err ( & self , msg : impl Into < DiagMessage > ) -> Diag < ' _ > {
463
+ self . tcx . dcx ( ) . struct_span_err ( self . span ( ) , msg)
464
+ }
465
+
466
+ #[ track_caller]
467
+ fn err ( & self , msg : impl Into < DiagMessage > ) {
468
+ self . tcx . dcx ( ) . span_err ( self . span ( ) , msg) ;
469
+ }
470
+
471
+ fn emit ( & mut self ) -> std:: cell:: RefMut < ' _ , rspirv:: dr:: Builder > {
472
+ match self {
473
+ Self :: Global ( cx, _) => cx. emit_global ( ) ,
474
+ Self :: Local ( bx) => bx. emit ( ) ,
475
+ }
476
+ }
477
+ }
478
+
479
+ impl < ' cx , ' tcx > InlineAsmCx < ' _ , ' cx , ' tcx > {
291
480
fn lex_word < ' a > ( & self , line : & mut std:: str:: Chars < ' a > ) -> Option < Token < ' a , ' cx , ' tcx > > {
292
481
loop {
293
482
let start = line. as_str ( ) ;
@@ -547,7 +736,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
547
736
} ;
548
737
let inst_class = inst_name
549
738
. strip_prefix ( "Op" )
550
- . and_then ( |n| self . cx . instruction_table . table . get ( n) ) ;
739
+ . and_then ( |n| self . instruction_table . table . get ( n) ) ;
551
740
let inst_class = if let Some ( inst) = inst_class {
552
741
inst
553
742
} else {
@@ -571,13 +760,12 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
571
760
}
572
761
self . insert_inst ( id_map, defined_ids, asm_block, instruction) ;
573
762
if let Some ( OutRegister :: Place ( place) ) = out_register {
763
+ let place = match self {
764
+ Self :: Global ( ..) => unreachable ! ( ) ,
765
+ Self :: Local ( bx) => place. val . llval . def ( bx) ,
766
+ } ;
574
767
self . emit ( )
575
- . store (
576
- place. val . llval . def ( self ) ,
577
- result_id. unwrap ( ) ,
578
- None ,
579
- std:: iter:: empty ( ) ,
580
- )
768
+ . store ( place, result_id. unwrap ( ) , None , std:: iter:: empty ( ) )
581
769
. unwrap ( ) ;
582
770
}
583
771
}
@@ -1093,7 +1281,10 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
1093
1281
Token :: Placeholder ( hole, span) => match hole {
1094
1282
InlineAsmOperandRef :: In { reg, value } => {
1095
1283
self . check_reg ( span, reg) ;
1096
- Some ( value. immediate ( ) . def ( self ) )
1284
+ match self {
1285
+ Self :: Global ( ..) => unreachable ! ( ) ,
1286
+ Self :: Local ( bx) => Some ( value. immediate ( ) . def ( bx) ) ,
1287
+ }
1097
1288
}
1098
1289
InlineAsmOperandRef :: Out {
1099
1290
reg,
@@ -1113,7 +1304,10 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
1113
1304
out_place : _,
1114
1305
} => {
1115
1306
self . check_reg ( span, reg) ;
1116
- Some ( in_value. immediate ( ) . def ( self ) )
1307
+ match self {
1308
+ Self :: Global ( ..) => unreachable ! ( ) ,
1309
+ Self :: Local ( bx) => Some ( in_value. immediate ( ) . def ( bx) ) ,
1310
+ }
1117
1311
}
1118
1312
InlineAsmOperandRef :: Const { string : _ } => {
1119
1313
self . tcx
0 commit comments