Skip to content

Commit 14435cb

Browse files
committed
WIP: spirv_asm: introduce InlineAsmCx to allow implementing global_asm!.
1 parent 9046395 commit 14435cb

File tree

2 files changed

+209
-43
lines changed

2 files changed

+209
-43
lines changed

crates/rustc_codegen_spirv/src/builder/spirv_asm.rs

Lines changed: 208 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
1818
use rustc_codegen_ssa::mir::operand::OperandValue;
1919
use rustc_codegen_ssa::mir::place::PlaceRef;
2020
use rustc_codegen_ssa::traits::{
21-
AsmBuilderMethods, BackendTypes, BuilderMethods, InlineAsmOperandRef,
21+
AsmBuilderMethods, AsmCodegenMethods, BackendTypes, BuilderMethods, GlobalAsmOperandRef,
22+
InlineAsmOperandRef,
2223
};
2324
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
25+
use rustc_errors::{Diag, DiagMessage};
2426
use rustc_middle::{bug, ty::Instance};
2527
use rustc_span::{DUMMY_SP, Span};
2628
use rustc_target::asm::{InlineAsmRegClass, InlineAsmRegOrRegClass, SpirVInlineAsmRegClass};
@@ -68,6 +70,153 @@ fn inline_asm_operand_ref_clone<'tcx, B: BackendTypes + ?Sized>(
6870
}
6971
}
7072

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+
71220
impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
72221
/* Example asm and the template it compiles to:
73222
asm!(
@@ -103,7 +252,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
103252
const SUPPORTED_OPTIONS: InlineAsmOptions = InlineAsmOptions::NORETURN;
104253
let unsupported_options = options & !SUPPORTED_OPTIONS;
105254
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:?}"));
107256
}
108257

109258
// HACK(eddyb) get more accurate pointers types, for pointer operands,
@@ -165,7 +314,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
165314
tokens.push(vec![]);
166315
}
167316
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) {
169318
tokens.last_mut().unwrap().push(token);
170319
}
171320
}
@@ -222,7 +371,7 @@ impl<'a, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'tcx> {
222371
// which is an unfortunate interaction, but perhaps avoidable?
223372
AsmBlock::End(_) => {}
224373
}
225-
self.codegen_asm(
374+
InlineAsmCx::Local(self).codegen_asm(
226375
&mut id_map,
227376
&mut defined_ids,
228377
&mut id_to_type_map,
@@ -287,7 +436,47 @@ enum AsmBlock {
287436
End(Op),
288437
}
289438

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> {
291480
fn lex_word<'a>(&self, line: &mut std::str::Chars<'a>) -> Option<Token<'a, 'cx, 'tcx>> {
292481
loop {
293482
let start = line.as_str();
@@ -547,7 +736,7 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
547736
};
548737
let inst_class = inst_name
549738
.strip_prefix("Op")
550-
.and_then(|n| self.cx.instruction_table.table.get(n));
739+
.and_then(|n| self.instruction_table.table.get(n));
551740
let inst_class = if let Some(inst) = inst_class {
552741
inst
553742
} else {
@@ -571,13 +760,12 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
571760
}
572761
self.insert_inst(id_map, defined_ids, asm_block, instruction);
573762
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+
};
574767
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())
581769
.unwrap();
582770
}
583771
}
@@ -1093,7 +1281,10 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
10931281
Token::Placeholder(hole, span) => match hole {
10941282
InlineAsmOperandRef::In { reg, value } => {
10951283
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+
}
10971288
}
10981289
InlineAsmOperandRef::Out {
10991290
reg,
@@ -1113,7 +1304,10 @@ impl<'cx, 'tcx> Builder<'cx, 'tcx> {
11131304
out_place: _,
11141305
} => {
11151306
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+
}
11171311
}
11181312
InlineAsmOperandRef::Const { string: _ } => {
11191313
self.tcx

crates/rustc_codegen_spirv/src/codegen_cx/mod.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,8 @@ use itertools::Itertools as _;
1919
use rspirv::dr::{Module, Operand};
2020
use rspirv::spirv::{Decoration, LinkageType, Word};
2121
use rustc_abi::{AddressSpace, HasDataLayout, TargetDataLayout};
22-
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
2322
use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind};
24-
use rustc_codegen_ssa::traits::{
25-
AsmCodegenMethods, BackendTypes, DebugInfoCodegenMethods, GlobalAsmOperandRef,
26-
MiscCodegenMethods,
27-
};
23+
use rustc_codegen_ssa::traits::{BackendTypes, DebugInfoCodegenMethods, MiscCodegenMethods};
2824
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2925
use rustc_hir::def_id::DefId;
3026
use rustc_middle::mir;
@@ -934,27 +930,3 @@ impl<'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'tcx> {
934930
todo!()
935931
}
936932
}
937-
938-
impl<'tcx> AsmCodegenMethods<'tcx> for CodegenCx<'tcx> {
939-
fn codegen_global_asm(
940-
&mut self,
941-
_template: &[InlineAsmTemplatePiece],
942-
_operands: &[GlobalAsmOperandRef<'tcx>],
943-
_options: InlineAsmOptions,
944-
line_spans: &[Span],
945-
) {
946-
self.tcx.dcx().span_fatal(
947-
line_spans.first().copied().unwrap_or_default(),
948-
"[Rust-GPU] `global_asm!` not yet supported",
949-
);
950-
}
951-
952-
// FIXME(eddyb) should this method be implemented as just symbol mangling,
953-
// or renamed upstream into something much more specific?
954-
fn mangled_name(&self, instance: Instance<'tcx>) -> String {
955-
self.tcx.dcx().span_bug(
956-
self.tcx.def_span(instance.def_id()),
957-
"[Rust-GPU] `#[naked] fn` not yet supported",
958-
)
959-
}
960-
}

0 commit comments

Comments
 (0)