Skip to content

Commit ec1ad61

Browse files
committed
Implement AST lowering for asm!
1 parent d5b1501 commit ec1ad61

File tree

4 files changed

+320
-6
lines changed

4 files changed

+320
-6
lines changed

src/librustc_ast_lowering/expr.rs

Lines changed: 294 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericAr
33
use rustc_ast::ast::*;
44
use rustc_ast::attr;
55
use rustc_ast::ptr::P as AstP;
6+
use rustc_data_structures::fx::FxHashMap;
67
use rustc_data_structures::stack::ensure_sufficient_stack;
78
use rustc_data_structures::thin_vec::ThinVec;
89
use rustc_errors::struct_span_err;
910
use rustc_hir as hir;
1011
use rustc_hir::def::Res;
1112
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
1213
use rustc_span::symbol::{sym, Ident, Symbol};
14+
use rustc_target::asm;
15+
use std::collections::hash_map::Entry;
16+
use std::fmt::Write;
1317

1418
impl<'hir> LoweringContext<'_, 'hir> {
1519
fn lower_exprs(&mut self, exprs: &[AstP<Expr>]) -> &'hir [hir::Expr<'hir>] {
@@ -175,7 +179,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
175179
let e = e.as_ref().map(|x| self.lower_expr(x));
176180
hir::ExprKind::Ret(e)
177181
}
178-
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_asm(asm),
182+
ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
183+
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
179184
ExprKind::Struct(ref path, ref fields, ref maybe_expr) => {
180185
let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x));
181186
hir::ExprKind::Struct(
@@ -968,7 +973,294 @@ impl<'hir> LoweringContext<'_, 'hir> {
968973
result
969974
}
970975

971-
fn lower_expr_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
976+
fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> {
977+
let asm_arch = if let Some(asm_arch) = self.sess.asm_arch {
978+
asm_arch
979+
} else {
980+
struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit();
981+
return hir::ExprKind::Err;
982+
};
983+
984+
// Lower operands to HIR, filter_map skips any operands with invalid
985+
// register classes.
986+
let sess = self.sess;
987+
let operands: Vec<_> = asm
988+
.operands
989+
.iter()
990+
.filter_map(|(op, op_sp)| {
991+
let lower_reg = |reg| {
992+
Some(match reg {
993+
InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg(
994+
asm::InlineAsmReg::parse(
995+
asm_arch,
996+
|feature| {
997+
self.sess.target_features.contains(&Symbol::intern(feature))
998+
},
999+
s,
1000+
)
1001+
.map_err(|e| {
1002+
let msg = format!("invalid register `{}`: {}", s.as_str(), e);
1003+
sess.struct_span_err(*op_sp, &msg).emit();
1004+
})
1005+
.ok()?,
1006+
),
1007+
InlineAsmRegOrRegClass::RegClass(s) => {
1008+
asm::InlineAsmRegOrRegClass::RegClass(
1009+
asm::InlineAsmRegClass::parse(asm_arch, s)
1010+
.map_err(|e| {
1011+
let msg = format!(
1012+
"invalid register class `{}`: {}",
1013+
s.as_str(),
1014+
e
1015+
);
1016+
sess.struct_span_err(*op_sp, &msg).emit();
1017+
})
1018+
.ok()?,
1019+
)
1020+
}
1021+
})
1022+
};
1023+
let op = match op {
1024+
InlineAsmOperand::In { reg, expr } => hir::InlineAsmOperand::In {
1025+
reg: lower_reg(*reg)?,
1026+
expr: self.lower_expr_mut(expr),
1027+
},
1028+
InlineAsmOperand::Out { reg, late, expr } => hir::InlineAsmOperand::Out {
1029+
reg: lower_reg(*reg)?,
1030+
late: *late,
1031+
expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
1032+
},
1033+
InlineAsmOperand::InOut { reg, late, expr } => hir::InlineAsmOperand::InOut {
1034+
reg: lower_reg(*reg)?,
1035+
late: *late,
1036+
expr: self.lower_expr_mut(expr),
1037+
},
1038+
InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
1039+
hir::InlineAsmOperand::SplitInOut {
1040+
reg: lower_reg(*reg)?,
1041+
late: *late,
1042+
in_expr: self.lower_expr_mut(in_expr),
1043+
out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
1044+
}
1045+
}
1046+
InlineAsmOperand::Const { expr } => {
1047+
hir::InlineAsmOperand::Const { expr: self.lower_expr_mut(expr) }
1048+
}
1049+
InlineAsmOperand::Sym { expr } => {
1050+
hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) }
1051+
}
1052+
};
1053+
Some(op)
1054+
})
1055+
.collect();
1056+
1057+
// Stop if there were any errors when lowering the register classes
1058+
if operands.len() != asm.operands.len() {
1059+
return hir::ExprKind::Err;
1060+
}
1061+
1062+
// Validate template modifiers against the register classes for the operands
1063+
for p in &asm.template {
1064+
if let asm::InlineAsmTemplatePiece::Placeholder {
1065+
operand_idx,
1066+
modifier: Some(modifier),
1067+
span: placeholder_span,
1068+
} = *p
1069+
{
1070+
let op_sp = asm.operands[operand_idx].1;
1071+
match &operands[operand_idx] {
1072+
hir::InlineAsmOperand::In { reg, .. }
1073+
| hir::InlineAsmOperand::Out { reg, .. }
1074+
| hir::InlineAsmOperand::InOut { reg, .. }
1075+
| hir::InlineAsmOperand::SplitInOut { reg, .. } => {
1076+
let class = reg.reg_class();
1077+
let valid_modifiers = class.valid_modifiers(asm_arch);
1078+
if !valid_modifiers.contains(&modifier) {
1079+
let mut err = sess.struct_span_err(
1080+
placeholder_span,
1081+
"invalid asm template modifier for this register class",
1082+
);
1083+
err.span_label(placeholder_span, "template modifier");
1084+
err.span_label(op_sp, "argument");
1085+
if !valid_modifiers.is_empty() {
1086+
let mut mods = format!("`{}`", valid_modifiers[0]);
1087+
for m in &valid_modifiers[1..] {
1088+
let _ = write!(mods, ", `{}`", m);
1089+
}
1090+
err.note(&format!(
1091+
"the `{}` register class supports \
1092+
the following template modifiers: {}",
1093+
class.name(),
1094+
mods
1095+
));
1096+
} else {
1097+
err.note(&format!(
1098+
"the `{}` register class does not support template modifiers",
1099+
class.name()
1100+
));
1101+
}
1102+
err.emit();
1103+
}
1104+
}
1105+
hir::InlineAsmOperand::Const { .. } => {
1106+
let mut err = sess.struct_span_err(
1107+
placeholder_span,
1108+
"asm template modifiers are not allowed for `const` arguments",
1109+
);
1110+
err.span_label(placeholder_span, "template modifier");
1111+
err.span_label(op_sp, "argument");
1112+
err.emit();
1113+
}
1114+
hir::InlineAsmOperand::Sym { .. } => {
1115+
let mut err = sess.struct_span_err(
1116+
placeholder_span,
1117+
"asm template modifiers are not allowed for `sym` arguments",
1118+
);
1119+
err.span_label(placeholder_span, "template modifier");
1120+
err.span_label(op_sp, "argument");
1121+
err.emit();
1122+
}
1123+
}
1124+
}
1125+
}
1126+
1127+
let mut used_input_regs = FxHashMap::default();
1128+
let mut used_output_regs = FxHashMap::default();
1129+
for (idx, op) in operands.iter().enumerate() {
1130+
let op_sp = asm.operands[idx].1;
1131+
if let Some(reg) = op.reg() {
1132+
// Validate register classes against currently enabled target
1133+
// features. We check that at least one type is available for
1134+
// the current target.
1135+
let reg_class = reg.reg_class();
1136+
let mut required_features = vec![];
1137+
for &(_, feature) in reg_class.supported_types(asm_arch) {
1138+
if let Some(feature) = feature {
1139+
if self.sess.target_features.contains(&Symbol::intern(feature)) {
1140+
required_features.clear();
1141+
break;
1142+
} else {
1143+
required_features.push(feature);
1144+
}
1145+
} else {
1146+
required_features.clear();
1147+
break;
1148+
}
1149+
}
1150+
required_features.sort();
1151+
required_features.dedup();
1152+
match &required_features[..] {
1153+
[] => {}
1154+
[feature] => {
1155+
let msg = format!(
1156+
"register class `{}` requires the `{}` target feature",
1157+
reg_class.name(),
1158+
feature
1159+
);
1160+
sess.struct_span_err(op_sp, &msg).emit();
1161+
}
1162+
features => {
1163+
let msg = format!(
1164+
"register class `{}` requires at least one target feature: {}",
1165+
reg_class.name(),
1166+
features.join(", ")
1167+
);
1168+
sess.struct_span_err(op_sp, &msg).emit();
1169+
}
1170+
}
1171+
1172+
// Check for conflicts between explicit register operands.
1173+
if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
1174+
let (input, output) = match op {
1175+
hir::InlineAsmOperand::In { .. } => (true, false),
1176+
// Late output do not conflict with inputs, but normal outputs do
1177+
hir::InlineAsmOperand::Out { late, .. } => (!late, true),
1178+
hir::InlineAsmOperand::InOut { .. }
1179+
| hir::InlineAsmOperand::SplitInOut { .. } => (true, true),
1180+
hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::Sym { .. } => {
1181+
unreachable!()
1182+
}
1183+
};
1184+
1185+
// Flag to output the error only once per operand
1186+
let mut skip = false;
1187+
reg.overlapping_regs(|r| {
1188+
let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
1189+
input| {
1190+
match used_regs.entry(r) {
1191+
Entry::Occupied(o) => {
1192+
if !skip {
1193+
skip = true;
1194+
1195+
let idx2 = *o.get();
1196+
let op2 = &operands[idx2];
1197+
let op_sp2 = asm.operands[idx2].1;
1198+
let reg2 = match op2.reg() {
1199+
Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
1200+
_ => unreachable!(),
1201+
};
1202+
1203+
let msg = format!(
1204+
"register `{}` conflicts with register `{}`",
1205+
reg.name(),
1206+
reg2.name()
1207+
);
1208+
let mut err = sess.struct_span_err(op_sp, &msg);
1209+
err.span_label(
1210+
op_sp,
1211+
&format!("register `{}`", reg.name()),
1212+
);
1213+
err.span_label(
1214+
op_sp2,
1215+
&format!("register `{}`", reg2.name()),
1216+
);
1217+
1218+
match (op, op2) {
1219+
(
1220+
hir::InlineAsmOperand::In { .. },
1221+
hir::InlineAsmOperand::Out { late, .. },
1222+
)
1223+
| (
1224+
hir::InlineAsmOperand::Out { late, .. },
1225+
hir::InlineAsmOperand::In { .. },
1226+
) => {
1227+
assert!(!*late);
1228+
let out_op_sp = if input { op_sp2 } else { op_sp };
1229+
let msg = &format!(
1230+
"use `lateout` instead of \
1231+
`out` to avoid conflict"
1232+
);
1233+
err.span_help(out_op_sp, msg);
1234+
}
1235+
_ => {}
1236+
}
1237+
1238+
err.emit();
1239+
}
1240+
}
1241+
Entry::Vacant(v) => {
1242+
v.insert(idx);
1243+
}
1244+
}
1245+
};
1246+
if input {
1247+
check(&mut used_input_regs, true);
1248+
}
1249+
if output {
1250+
check(&mut used_output_regs, false);
1251+
}
1252+
});
1253+
}
1254+
}
1255+
}
1256+
1257+
let operands = self.arena.alloc_from_iter(operands);
1258+
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
1259+
let hir_asm = hir::InlineAsm { template, operands, options: asm.options };
1260+
hir::ExprKind::InlineAsm(self.arena.alloc(hir_asm))
1261+
}
1262+
1263+
fn lower_expr_llvm_asm(&mut self, asm: &LlvmInlineAsm) -> hir::ExprKind<'hir> {
9721264
let inner = hir::LlvmInlineAsmInner {
9731265
inputs: asm.inputs.iter().map(|&(c, _)| c).collect(),
9741266
outputs: asm

src/librustc_hir/arena.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ macro_rules! arena_types {
1414
// HIR types
1515
[few] hir_krate: rustc_hir::Crate<$tcx>,
1616
[] arm: rustc_hir::Arm<$tcx>,
17+
[] asm_operand: rustc_hir::InlineAsmOperand<$tcx>,
18+
[] asm_template: rustc_target::asm::InlineAsmTemplatePiece,
1719
[] attribute: rustc_ast::ast::Attribute,
1820
[] block: rustc_hir::Block<$tcx>,
1921
[] bare_fn_ty: rustc_hir::BareFnTy<$tcx>,
@@ -28,7 +30,8 @@ macro_rules! arena_types {
2830
[] fn_decl: rustc_hir::FnDecl<$tcx>,
2931
[] foreign_item: rustc_hir::ForeignItem<$tcx>,
3032
[] impl_item_ref: rustc_hir::ImplItemRef<$tcx>,
31-
[] inline_asm: rustc_hir::LlvmInlineAsm<$tcx>,
33+
[few] inline_asm: rustc_hir::InlineAsm<$tcx>,
34+
[few] llvm_inline_asm: rustc_hir::LlvmInlineAsm<$tcx>,
3235
[] local: rustc_hir::Local<$tcx>,
3336
[few] macro_def: rustc_hir::MacroDef<$tcx>,
3437
[] param: rustc_hir::Param<$tcx>,

src/librustc_interface/util.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,15 @@ use std::{panic, thread};
4242
/// features is available on the target machine, by querying LLVM.
4343
pub fn add_configuration(
4444
cfg: &mut CrateConfig,
45-
sess: &Session,
45+
sess: &mut Session,
4646
codegen_backend: &dyn CodegenBackend,
4747
) {
4848
let tf = sym::target_feature;
4949

50-
cfg.extend(codegen_backend.target_features(sess).into_iter().map(|feat| (tf, Some(feat))));
50+
let target_features = codegen_backend.target_features(sess);
51+
sess.target_features.extend(target_features.iter().cloned());
52+
53+
cfg.extend(target_features.into_iter().map(|feat| (tf, Some(feat))));
5154

5255
if sess.crt_static(None) {
5356
cfg.insert((tf, Some(Symbol::intern("crt-static"))));
@@ -75,7 +78,7 @@ pub fn create_session(
7578
let codegen_backend = get_codegen_backend(&sess);
7679

7780
let mut cfg = config::build_configuration(&sess, config::to_crate_config(cfg));
78-
add_configuration(&mut cfg, &sess, &*codegen_backend);
81+
add_configuration(&mut cfg, &mut sess, &*codegen_backend);
7982
sess.parse_sess.config = cfg;
8083

8184
(Lrc::new(sess), Lrc::new(codegen_backend), source_map)

0 commit comments

Comments
 (0)