1use std::cell::Cell;
174use std::collections::{BTreeSet, HashMap, HashSet};
175use std::str::FromStr;
176
177use proc_macro2::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
178use quote::quote_spanned;
179
180use crate::Options;
181use crate::capture::{SourceMap, SourceRange};
182use crate::cfg::{BasicBlock, BlockId, Cfg, Terminator};
183use crate::ir::{
184 self, AtomicClass, BinOp, Body, BreakTarget, Callee, CmpOp, ConstValue, Expr, ExprKind,
185 Function, LogicalOp, LoopId, NEVER_RAW, Place, PlaceKind, Program, RecordKind, Stmt, Storage,
186 Switch, Ty,
187};
188
189pub fn generate(program: &Program, map: &SourceMap, options: &Options) -> TokenStream {
191 let mut cg = Codegen::new(program, map, options);
192 let mut out = cg.type_items();
193 out.extend(cg.extern_block());
194 for var in &program.statics {
195 out.extend(cg.static_item(var));
196 }
197 let mut initialisers = TokenStream::new();
198 for func in &program.functions {
199 if func.body.is_some() && !cg.beyond_toolchain(func) {
200 out.extend(cg.function_item(func));
201 if let Some(kind) = func.init_kind {
202 initialisers.extend(cg.init_array_item(func, kind));
203 }
204 }
205 }
206 if !initialisers.is_empty() {
207 out.extend(cg.init_array_guard());
208 out.extend(initialisers);
209 }
210 if out.is_empty() {
211 return out;
214 }
215 let mut items = cg.data_model_check();
216 if cg.uses_cleanup.get() {
217 items.extend(cg.cleanup_guard_item(Span::call_site()));
218 }
219 items.extend(out);
220 items
221}
222
223pub fn generate_stubs(program: &Program, map: &SourceMap, options: &Options) -> TokenStream {
230 let mut cg = Codegen::new(program, map, options);
231 let mut out = cg.type_items();
232 out.extend(cg.extern_block());
233 for var in &program.statics {
234 out.extend(cg.static_item(var));
235 }
236 for func in &program.functions {
237 if !func.is_extern() && !cg.beyond_toolchain(func) {
238 out.extend(cg.stub_item(func));
239 }
240 }
241 out
242}
243
244mod prec {
254 pub const BLOCK: u8 = 0;
257 pub const LOWEST: u8 = 0;
259 pub const OR: u8 = 2;
260 pub const AND: u8 = 3;
261 pub const CMP: u8 = 4;
262 pub const BIT_OR: u8 = 5;
263 pub const BIT_XOR: u8 = 6;
264 pub const BIT_AND: u8 = 7;
265 pub const SUM: u8 = 9;
266 pub const PRODUCT: u8 = 10;
267 pub const CAST: u8 = 11;
268 pub const UNARY: u8 = 12;
269 pub const CALL: u8 = 13;
270 pub const ATOM: u8 = 14;
271}
272
273struct Value {
275 tokens: TokenStream,
276 prec: u8,
277 bare_integer: bool,
280 ends_with_type: bool,
286}
287
288impl Value {
289 fn new(tokens: TokenStream, prec: u8) -> Self {
290 Self {
291 tokens,
292 prec,
293 bare_integer: false,
294 ends_with_type: false,
295 }
296 }
297
298 fn atom(tokens: TokenStream) -> Self {
299 Self::new(tokens, prec::ATOM)
300 }
301
302 fn type_end(mut self, flag: bool) -> Self {
304 self.ends_with_type = flag;
305 self
306 }
307
308 fn at(self, min: u8, span: Span) -> TokenStream {
311 if self.prec >= min {
312 return self.tokens;
313 }
314 parenthesize(self.tokens, span)
315 }
316
317 fn at_condition(self, span: Span) -> TokenStream {
323 if self.prec > prec::BLOCK {
324 return self.tokens;
325 }
326 parenthesize(self.tokens, span)
327 }
328}
329
330fn parenthesize(tokens: TokenStream, span: Span) -> TokenStream {
331 let mut group = Group::new(Delimiter::Parenthesis, tokens);
332 group.set_span(span);
333 TokenStream::from(TokenTree::Group(group))
334}
335
336fn starts_with_minus(tokens: &TokenStream) -> bool {
342 matches!(
343 tokens.clone().into_iter().next(),
344 Some(TokenTree::Punct(punct)) if punct.as_char() == '-'
345 )
346}
347
348fn braced(tokens: TokenStream, span: Span) -> TokenStream {
349 let mut group = Group::new(Delimiter::Brace, tokens);
350 group.set_span(span);
351 TokenStream::from(TokenTree::Group(group))
352}
353
354fn bracketed(tokens: TokenStream, span: Span) -> TokenStream {
355 let mut group = Group::new(Delimiter::Bracket, tokens);
356 group.set_span(span);
357 TokenStream::from(TokenTree::Group(group))
358}
359
360const LEGACY_STDIO_DEFINITIONS: &str = "legacy_stdio_definitions";
374
375const LEGACY_STDIO: &[(&str, &str)] = &[
413 ("printf", LEGACY_STDIO_DEFINITIONS),
414 ("fprintf", LEGACY_STDIO_DEFINITIONS),
415 ("sprintf", LEGACY_STDIO_DEFINITIONS),
416 ("snprintf", LEGACY_STDIO_DEFINITIONS),
417 ("vprintf", LEGACY_STDIO_DEFINITIONS),
418 ("vfprintf", LEGACY_STDIO_DEFINITIONS),
419 ("vsprintf", LEGACY_STDIO_DEFINITIONS),
420 ("vsnprintf", LEGACY_STDIO_DEFINITIONS),
421 ("scanf", LEGACY_STDIO_DEFINITIONS),
422 ("fscanf", LEGACY_STDIO_DEFINITIONS),
423 ("sscanf", LEGACY_STDIO_DEFINITIONS),
424 ("vscanf", LEGACY_STDIO_DEFINITIONS),
425 ("vfscanf", LEGACY_STDIO_DEFINITIONS),
426 ("vsscanf", LEGACY_STDIO_DEFINITIONS),
427 ("wprintf", LEGACY_STDIO_DEFINITIONS),
428 ("fwprintf", LEGACY_STDIO_DEFINITIONS),
429 ("swprintf", LEGACY_STDIO_DEFINITIONS),
430 ("vwprintf", LEGACY_STDIO_DEFINITIONS),
431 ("vfwprintf", LEGACY_STDIO_DEFINITIONS),
432 ("vswprintf", LEGACY_STDIO_DEFINITIONS),
433 ("wscanf", LEGACY_STDIO_DEFINITIONS),
434 ("fwscanf", LEGACY_STDIO_DEFINITIONS),
435 ("swscanf", LEGACY_STDIO_DEFINITIONS),
436 ("vwscanf", LEGACY_STDIO_DEFINITIONS),
437 ("vfwscanf", LEGACY_STDIO_DEFINITIONS),
438 ("vswscanf", LEGACY_STDIO_DEFINITIONS),
439];
440
441const MSVC_RENAMED: &[(&str, &str)] = &[
480 ("time", "_time64"),
481 ("difftime", "_difftime64"),
482 ("mktime", "_mktime64"),
483 ("localtime", "_localtime64"),
484 ("gmtime", "_gmtime64"),
485 ("ctime", "_ctime64"),
486 ("timespec_get", "_timespec64_get"),
487 ("hypotf", "_hypotf"),
488];
489
490const RUST_KEYWORDS: &[&str] = &[
496 "abstract", "as", "async", "await", "become", "box", "break", "const", "continue", "crate",
497 "do", "dyn", "else", "enum", "extern", "false", "final", "fn", "for", "gen", "if", "impl",
498 "in", "let", "loop", "macro", "match", "mod", "move", "mut", "override", "priv", "pub", "ref",
499 "return", "self", "static", "struct", "super", "trait", "true", "try", "type", "typeof",
500 "unsafe", "unsized", "use", "virtual", "where", "while", "yield", "Self",
501];
502
503const PRELUDE_PATTERNS: &[&str] = &["Some", "None", "Ok", "Err"];
519
520fn is_path_segment(segment: &str) -> bool {
527 let mut chars = segment.chars();
528 chars
529 .next()
530 .is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
531 && chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
532 && !RUST_KEYWORDS.contains(&segment)
533 && !NEVER_RAW.contains(&segment)
534}
535
536pub fn is_crate_path(path: &str) -> bool {
546 const PATH_KEYWORDS: &[&str] = &["crate", "self", "super"];
549
550 let body = path.strip_prefix("::").unwrap_or(path);
551 if body.is_empty() {
552 return false;
553 }
554 body.split("::")
555 .all(|segment| PATH_KEYWORDS.contains(&segment) || is_path_segment(segment))
556}
557
558fn c_ident(name: &str, span: Span) -> Ident {
572 let spelled = rust_spelling(name);
573 let name = spelled.as_ref();
574 if NEVER_RAW.contains(&name) {
575 return Ident::new(&format!("{name}_"), span);
576 }
577 if RUST_KEYWORDS.contains(&name) {
578 return Ident::new_raw(name, span);
579 }
580 Ident::new(name, span)
581}
582
583pub const DOLLAR: &str = "_dollar_";
592
593fn rust_spelling(name: &str) -> std::borrow::Cow<'_, str> {
596 if name.contains('$') {
597 std::borrow::Cow::Owned(name.replace('$', DOLLAR))
598 } else {
599 std::borrow::Cow::Borrowed(name)
600 }
601}
602
603fn respelled(name: &str) -> bool {
609 name.contains('$') || NEVER_RAW.contains(&name)
610}
611
612fn plain_spelling(name: &str) -> String {
615 let spelled = rust_spelling(name);
616 if NEVER_RAW.contains(&spelled.as_ref()) {
617 return format!("{spelled}_");
618 }
619 spelled.into_owned()
620}
621
622struct Names {
649 renamed: HashMap<String, String>,
653}
654
655impl Names {
656 fn new(program: &Program) -> Self {
658 let mut spelled: Vec<&str> = Vec::new();
659 for object in &program.objects {
660 spelled.push(&object.name);
661 match &object.storage {
662 Storage::Static { item_name, .. }
663 | Storage::ThreadLocal { item_name, .. }
664 | Storage::Extern { item_name } => spelled.push(item_name),
665 Storage::Automatic => {}
666 }
667 }
668 for func in &program.functions {
669 spelled.push(&func.name);
670 spelled.push(func.item_name());
671 spelled.extend(func.param_names.iter().flatten().map(String::as_str));
672 if let Some(Body::Cfg(cfg)) = &func.body {
675 spelled.extend(cfg.locals.iter().map(|local| local.rust_name.as_str()));
676 }
677 }
678 for record in program.types.records() {
679 spelled.extend(record.tag.as_deref());
680 spelled.push(&record.rust_name);
681 for field in &record.fields {
682 spelled.push(&field.name);
683 if let Some(bits) = &field.bits {
684 spelled.push(&bits.getter);
685 spelled.push(&bits.setter);
686 }
687 }
688 for field in &record.rust_fields {
689 match field {
690 ir::RustField::Bits { name, .. }
691 | ir::RustField::Pad { name, .. }
692 | ir::RustField::Align { name, .. } => spelled.push(name),
693 ir::RustField::Member(_) => {}
694 }
695 }
696 }
697 for def in program.types.enums() {
698 spelled.extend(def.tag.as_deref());
699 spelled.push(&def.rust_name);
700 }
701 for constant in &program.enum_constants {
702 spelled.push(&constant.name);
703 spelled.push(&constant.rust_name);
704 }
705 for typedef in &program.typedefs {
706 spelled.push(&typedef.rust_name);
707 }
708
709 Self {
710 renamed: unique_spellings(spelled),
711 }
712 }
713
714 fn ident(&self, name: &str, span: Span) -> Ident {
716 match self.renamed.get(name) {
717 Some(unique) => Ident::new(unique, span),
718 None => c_ident(name, span),
719 }
720 }
721
722 fn spelling<'n>(&'n self, name: &'n str) -> std::borrow::Cow<'n, str> {
724 match self.renamed.get(name) {
725 Some(unique) => std::borrow::Cow::Borrowed(unique.as_str()),
726 None => std::borrow::Cow::Owned(plain_spelling(name)),
727 }
728 }
729}
730
731fn unique_spellings<'n>(spelled: impl IntoIterator<Item = &'n str>) -> HashMap<String, String> {
740 let mut taken: HashSet<&str> = HashSet::new();
741 let mut changing: BTreeSet<&str> = BTreeSet::new();
742 for name in spelled {
743 if respelled(name) {
744 changing.insert(name);
745 } else {
746 taken.insert(name);
747 }
748 }
749 let mut renamed: HashMap<String, String> = HashMap::new();
750 let mut assigned: HashSet<String> = HashSet::new();
751 for name in changing {
752 let mut candidate = plain_spelling(name);
753 while taken.contains(candidate.as_str()) || assigned.contains(&candidate) {
754 candidate.push('_');
755 }
756 assigned.insert(candidate.clone());
757 renamed.insert(name.to_owned(), candidate);
758 }
759 renamed
760}
761
762fn is_label_name(name: &str) -> bool {
770 let mut chars = name.chars();
771 let starts = chars
772 .next()
773 .is_some_and(|c| c.is_ascii_alphabetic() || c == '_');
774 starts
775 && chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
776 && !RUST_KEYWORDS.contains(&name)
777 && !NEVER_RAW.contains(&name)
778 && !is_generated_label(name)
779}
780
781fn is_generated_label(name: &str) -> bool {
784 fn digits(text: &str) -> bool {
785 !text.is_empty() && text.bytes().all(|b| b.is_ascii_digit())
786 }
787
788 if name == "cfg" {
789 return true;
790 }
791 if let Some(rest) = name.strip_prefix('l')
792 && digits(rest.strip_suffix("_body").unwrap_or(rest))
793 {
794 return true;
795 }
796 if let Some(rest) = name.strip_prefix("sw") {
797 if digits(rest) {
798 return true;
799 }
800 if let Some((switch, case)) = rest.split_once("_c") {
801 return digits(switch) && digits(case);
802 }
803 }
804 false
805}
806
807fn collect_regions(stmts: &[Stmt], out: &mut Vec<(ir::LabelId, String)>) {
810 for stmt in stmts {
811 match stmt {
812 Stmt::Region(region) => {
813 out.push((region.label, region.name.clone()));
814 collect_regions(®ion.body, out);
815 }
816 Stmt::Block(items) => collect_regions(items, out),
817 Stmt::Label { body, .. } | Stmt::Case { body, .. } => {
818 collect_regions(std::slice::from_ref(body), out);
819 }
820 Stmt::If {
821 then_branch,
822 else_branch,
823 ..
824 } => {
825 collect_regions(std::slice::from_ref(then_branch), out);
826 if let Some(branch) = else_branch {
827 collect_regions(std::slice::from_ref(branch), out);
828 }
829 }
830 Stmt::While { body, .. } | Stmt::DoWhile { body, .. } => {
831 collect_regions(std::slice::from_ref(body), out);
832 }
833 Stmt::For { init, body, .. } => {
834 collect_regions(init, out);
835 collect_regions(std::slice::from_ref(body), out);
836 }
837 Stmt::Switch(switch) => {
838 collect_regions(&switch.prelude, out);
839 for group in &switch.groups {
840 collect_regions(&group.body, out);
841 }
842 }
843 Stmt::SwitchTree(switch) => collect_regions(std::slice::from_ref(&switch.body), out),
844 _ => {}
845 }
846 }
847}
848
849fn label_state(expr: &Expr) -> Option<ir::LabelId> {
881 match &expr.kind {
882 ExprKind::LabelAddr(id) => Some(*id),
883 ExprKind::Cast(inner) => label_state(inner),
884 _ => None,
885 }
886}
887
888fn align_wrapper_ident(align: u64, span: Span) -> Ident {
891 Ident::new(&format!("__cinrs_align_{align}"), span)
892}
893
894fn array_len(types: &ir::Types, ty: Ty) -> Option<u64> {
896 match ty {
897 Ty::Array(id) => Some(types.array_type(id).len),
898 _ => None,
899 }
900}
901
902fn cleanup_guard_ty() -> Ident {
905 Ident::new("__cinrs_cleanup", Span::mixed_site())
906}
907
908#[derive(Clone, Copy, PartialEq, Eq)]
914enum ContinueStyle {
915 Head,
917 BodyLabel,
919}
920
921struct LoweredPlace {
923 setup: TokenStream,
925 access: TokenStream,
927 bits: Option<BitAccess>,
930 unaligned: bool,
938 atomic: Option<(AtomicClass, Ty)>,
946}
947
948impl LoweredPlace {
949 fn plain(setup: TokenStream, access: TokenStream) -> Self {
951 Self {
952 setup,
953 access,
954 bits: None,
955 unaligned: false,
956 atomic: None,
957 }
958 }
959}
960
961enum PlaceRmw<'a> {
963 Compound {
965 op: BinOp,
967 value: &'a Expr,
969 compute: Ty,
971 },
972 Step {
974 dec: bool,
976 },
977}
978
979#[derive(Clone, Copy, PartialEq, Eq)]
981enum RmwValue {
982 None,
984 Old,
986 New,
988}
989
990fn rmw_of_binop(op: BinOp) -> Option<ir::AtomicRmw> {
993 Some(match op {
994 BinOp::Add => ir::AtomicRmw::Add,
995 BinOp::Sub => ir::AtomicRmw::Sub,
996 BinOp::BitAnd => ir::AtomicRmw::And,
997 BinOp::BitOr => ir::AtomicRmw::Or,
998 BinOp::BitXor => ir::AtomicRmw::Xor,
999 _ => return None,
1000 })
1001}
1002
1003struct BitAccess {
1005 getter: Ident,
1006 setter: Ident,
1007}
1008
1009struct BitWindow {
1016 read: TokenStream,
1018 shift: u32,
1020 mask: u128,
1022 word: TokenStream,
1024 word_bits: u32,
1027}
1028
1029#[derive(Clone, Copy, PartialEq, Eq)]
1031enum VaSource {
1032 None,
1034 Ellipsis,
1036 Param(ir::ObjectId),
1038 PtrParam(ir::ObjectId),
1040}
1041
1042struct Codegen<'a> {
1043 program: &'a Program,
1044 map: &'a SourceMap,
1045 options: &'a Options,
1046 continue_styles: HashMap<LoopId, ContinueStyle>,
1047 local_names: HashMap<ir::ObjectId, String>,
1052 env: HashMap<ir::ObjectId, ir::ObjectId>,
1059 reserved: HashSet<String>,
1061 names: Names,
1064 va_source: VaSource,
1065 ret_ty: Ty,
1066 in_cfg: bool,
1070 temporaries: u32,
1071 uses_int128: Cell<bool>,
1078 uses_complex: Cell<bool>,
1082 uses_cleanup: Cell<bool>,
1084 label_states: HashMap<ir::LabelId, u32>,
1093 region_names: HashMap<ir::LabelId, String>,
1097 region_kinds: HashMap<ir::LabelId, ir::RegionKind>,
1100}
1101
1102impl<'a> Codegen<'a> {
1103 fn new(program: &'a Program, map: &'a SourceMap, options: &'a Options) -> Self {
1104 let mut reserved: HashSet<String> =
1105 PRELUDE_PATTERNS.iter().map(|s| (*s).to_owned()).collect();
1106 for var in &program.statics {
1109 if let Some(item_name) = program.object(var.object).storage.item_name() {
1110 reserved.insert(item_name.to_owned());
1111 }
1112 }
1113 for constant in &program.enum_constants {
1114 reserved.insert(constant.rust_name.clone());
1115 }
1116 let mut label_states = HashMap::new();
1117 for func in &program.functions {
1118 if let Some(ir::Body::Cfg(cfg)) = &func.body {
1119 for (id, block) in &cfg.labels {
1120 label_states.insert(*id, block.0);
1121 }
1122 }
1123 }
1124 Self {
1125 program,
1126 map,
1127 options,
1128 continue_styles: HashMap::new(),
1129 local_names: HashMap::new(),
1130 env: HashMap::new(),
1131 reserved,
1132 names: Names::new(program),
1133 va_source: VaSource::None,
1134 ret_ty: Ty::Void,
1135 in_cfg: false,
1136 temporaries: 0,
1137 uses_int128: Cell::new(false),
1138 uses_complex: Cell::new(false),
1139 uses_cleanup: Cell::new(false),
1140 label_states,
1141 region_names: HashMap::new(),
1142 region_kinds: HashMap::new(),
1143 }
1144 }
1145
1146 fn beyond_toolchain(&self, func: &Function) -> bool {
1153 if self.options.c_variadic {
1154 return false;
1155 }
1156 (func.sig.variadic && !func.is_extern())
1157 || self.uses_va_list(func.sig.ret)
1158 || func.sig.params.iter().any(|ty| self.uses_va_list(*ty))
1159 }
1160
1161 fn uses_va_list(&self, ty: Ty) -> bool {
1163 match ty {
1164 Ty::VaList => true,
1165 Ty::Pointer(id) => self.uses_va_list(self.program.types.pointer_type(id).pointee),
1166 Ty::Array(id) => self.uses_va_list(self.program.types.array_type(id).elem),
1167 Ty::Func(id) => {
1168 let func = self.program.types.func_type(id);
1169 self.uses_va_list(func.ret) || func.params.iter().any(|ty| self.uses_va_list(*ty))
1170 }
1171 _ => false,
1172 }
1173 }
1174
1175 fn sp(&self, range: SourceRange) -> Span {
1176 self.map.span(range)
1177 }
1178
1179 fn c_ident(&self, name: &str, span: Span) -> Ident {
1185 self.names.ident(name, span)
1186 }
1187
1188 fn flexible_ident(&self, rust_name: &str, len: u64, span: Span) -> Ident {
1191 Ident::new(
1192 &format!("__cinrs_{}_{len}", self.names.spelling(rust_name)),
1193 span,
1194 )
1195 }
1196
1197 fn extern_object_ident(&self, symbol: &str, span: Span) -> Ident {
1205 let spelling = self.names.spelling(symbol);
1206 Ident::new(&self.program.extern_object_name(&spelling), span)
1207 }
1208
1209 fn temporary(&mut self) -> Ident {
1214 let name = format!("__cinrs_tmp{}", self.temporaries);
1215 self.temporaries += 1;
1216 Ident::new(&name, Span::mixed_site())
1217 }
1218
1219 fn temporary_at(&mut self, span: Span) -> Ident {
1232 let name = format!("__cinrs_tmp{}", self.temporaries);
1233 self.temporaries += 1;
1234 Ident::new(&name, span.resolved_at(Span::mixed_site()))
1235 }
1236
1237 fn label(&self, name: &str, span: Span) -> TokenStream {
1239 let mut tick = Punct::new('\'', Spacing::Joint);
1240 tick.set_span(span);
1241 let mut out = TokenStream::new();
1242 out.extend([
1243 TokenTree::Punct(tick),
1244 TokenTree::Ident(Ident::new(name, span)),
1245 ]);
1246 out
1247 }
1248
1249 fn ty(&self, ty: Ty, span: Span) -> TokenStream {
1253 let name = match ty {
1254 Ty::Void | Ty::Error => return quote_spanned! {span=> () },
1257 Ty::Bool => return primitive_ty("bool", span),
1261 Ty::Char => "c_char",
1262 Ty::SChar => "c_schar",
1263 Ty::UChar => "c_uchar",
1264 Ty::Short => "c_short",
1265 Ty::UShort => "c_ushort",
1266 Ty::Int => "c_int",
1267 Ty::UInt => "c_uint",
1268 Ty::Long => "c_long",
1269 Ty::ULong => "c_ulong",
1270 Ty::LongLong => "c_longlong",
1271 Ty::ULongLong => "c_ulonglong",
1272 Ty::Int128 => {
1278 self.uses_int128.set(true);
1279 return primitive_ty("i128", span);
1280 }
1281 Ty::UInt128 => {
1282 self.uses_int128.set(true);
1283 return primitive_ty("u128", span);
1284 }
1285 Ty::Float => "c_float",
1286 Ty::Double => "c_double",
1287 Ty::ComplexFloat | Ty::ComplexDouble => {
1292 self.uses_complex.set(true);
1293 let component =
1294 primitive_ty(if ty == Ty::ComplexFloat { "f32" } else { "f64" }, span);
1295 let rt = self.rt_path(span);
1296 return quote_spanned! {span=> #rt::Complex<#component> };
1297 }
1298 Ty::VaList => "VaList",
1301 Ty::Pointer(id) => {
1302 let pointer = self.program.types.pointer_type(id);
1303 if let Ty::Func(func) = pointer.pointee {
1304 let signature = self.fn_ty(func, span);
1308 return quote_spanned! {span=> ::core::option::Option<#signature> };
1309 }
1310 let pointee = self.pointee_ty(pointer.pointee, span);
1311 return if pointer.konst {
1312 quote_spanned! {span=> *const #pointee }
1313 } else {
1314 quote_spanned! {span=> *mut #pointee }
1315 };
1316 }
1317 Ty::Array(id) => {
1318 if self.program.types.is_vm(ty) {
1324 let step = self.ty(self.program.types.vm_step_ty(ty), span);
1325 return quote_spanned! {span=> *mut #step };
1326 }
1327 let array = self.program.types.array_type(id);
1328 let elem = self.ty(array.elem, span);
1329 let len = usize_literal(array.len, span);
1330 let inner = quote_spanned! {span=> #elem ; #len };
1331 return bracketed(inner, span);
1332 }
1333 Ty::Func(id) => return self.fn_ty(id, span),
1334 Ty::Record(id) => {
1335 let name = self.c_ident(&self.program.types.record(id).rust_name, span);
1336 return quote_spanned! {span=> #name };
1337 }
1338 Ty::Enum(id) => {
1339 let name = self.c_ident(&self.program.types.enum_def(id).rust_name, span);
1340 return quote_spanned! {span=> #name };
1341 }
1342 Ty::Atomic(id) => {
1348 let inner = self.program.types.atomic_inner(id);
1349 return self.ty(inner, span);
1350 }
1351 };
1352 let ident = Ident::new(name, span);
1353 quote_spanned! {span=> ::core::ffi::#ident }
1354 }
1355
1356 fn rt_path(&self, span: Span) -> TokenStream {
1364 let path = TokenStream::from_str(&self.program.crate_path)
1365 .unwrap_or_else(|_| TokenStream::from_str(ir::DEFAULT_CRATE_PATH).expect("valid"));
1366 let path = respan(path, span);
1367 quote_spanned! {span=> #path::rt }
1368 }
1369
1370 fn rt_complex(&self, name: &str, span: Span) -> TokenStream {
1372 let rt = self.rt_path(span);
1373 let ident = Ident::new(name, span);
1374 quote_spanned! {span=> #rt::complex::#ident }
1375 }
1376
1377 fn complex_suffix(ty: Ty) -> &'static str {
1379 if ty.complex_component() == Ty::Float {
1380 "f32"
1381 } else {
1382 "f64"
1383 }
1384 }
1385
1386 fn fn_ty(&self, id: ir::FuncTyId, span: Span) -> TokenStream {
1393 let func = self.program.types.func_type(id).clone();
1394 self.fn_ptr_ty(&func.params, func.variadic, func.ret, span)
1395 }
1396
1397 fn alloc_crate(&self, span: Span) -> Ident {
1407 let name = if self.program.no_std { "alloc" } else { "std" };
1408 Ident::new(name, span)
1409 }
1410
1411 fn vec_ty(&self, elem: TokenStream, span: Span) -> TokenStream {
1413 let krate = self.alloc_crate(span);
1414 quote_spanned! {span=> ::#krate::vec::Vec<#elem> }
1415 }
1416
1417 fn vec_new(&self, span: Span) -> TokenStream {
1419 let krate = self.alloc_crate(span);
1420 quote_spanned! {span=> ::#krate::vec::Vec::new() }
1421 }
1422
1423 fn vec_of(&self, value: TokenStream, len: TokenStream, span: Span) -> TokenStream {
1426 let krate = self.alloc_crate(span);
1427 quote_spanned! {span=> ::#krate::vec::from_elem(#value, #len) }
1428 }
1429
1430 fn pointee_ty(&self, ty: Ty, span: Span) -> TokenStream {
1437 if ty.is_void() {
1438 let ident = Ident::new("c_void", span);
1439 return quote_spanned! {span=> ::core::ffi::#ident };
1440 }
1441 if self.program.types.is_vm(ty) {
1446 return self.ty(self.program.types.vm_step_ty(ty), span);
1447 }
1448 self.ty(ty, span)
1449 }
1450
1451 fn type_items(&mut self) -> TokenStream {
1455 let mut out = self.align_wrapper_items();
1456 for record in self.program.types.records() {
1457 if !record.emit {
1458 continue;
1459 }
1460 out.extend(self.record_item(record));
1461 }
1462 out.extend(self.flexible_items());
1463 for def in self.program.types.enums() {
1464 if !def.emit {
1465 continue;
1466 }
1467 let span = self.sp(def.range);
1468 let name = self.c_ident(&def.rust_name, span);
1469 let int = self.ty(Ty::Int, span);
1470 out.extend(quote_spanned! {span=> pub type #name = #int; });
1473 }
1474 for constant in &self.program.enum_constants {
1475 let span = self.sp(constant.range);
1476 let name = self.c_ident(&constant.rust_name, span);
1477 let ty = self.ty(constant.ty, span);
1478 let value = bare_int_literal(constant.value, constant.ty, span);
1479 out.extend(quote_spanned! {span=> pub const #name: #ty = #value; });
1480 }
1481 for typedef in &self.program.typedefs {
1482 if self.uses_va_list(typedef.ty) {
1488 continue;
1489 }
1490 let span = self.sp(typedef.range);
1491 let name = self.c_ident(&typedef.rust_name, span);
1492 let ty = self.ty(typedef.ty, span);
1493 out.extend(quote_spanned! {span=> pub type #name = #ty; });
1494 }
1495 out
1496 }
1497
1498 fn align_wrapper_items(&self) -> TokenStream {
1516 let mut wanted: Vec<(u64, SourceRange)> = self
1517 .program
1518 .objects
1519 .iter()
1520 .filter_map(|object| Some((object.align?, object.range)))
1521 .collect();
1522 wanted.sort_by_key(|(align, _)| *align);
1523 wanted.dedup_by_key(|(align, _)| *align);
1524 let mut out = TokenStream::new();
1525 for (align, range) in wanted {
1526 let span = self.sp(range);
1527 let name = align_wrapper_ident(align, span);
1528 let literal = Literal::u64_unsuffixed(align);
1529 out.extend(quote_spanned! {span=>
1530 #[repr(C, align(#literal))]
1531 #[derive(Copy, Clone)]
1532 pub struct #name<T>(pub T);
1533 });
1534 }
1535 out
1536 }
1537
1538 fn flexible_items(&self) -> TokenStream {
1555 let mut wanted: Vec<(ir::RecordId, u64)> = self
1556 .program
1557 .objects
1558 .iter()
1559 .filter_map(|object| match (object.flexible_len, object.ty) {
1560 (Some(len), Ty::Record(record)) => Some((record, len)),
1561 _ => None,
1562 })
1563 .collect();
1564 wanted.sort_unstable_by_key(|(record, len)| (record.0, *len));
1565 wanted.dedup_by_key(|(record, len)| (record.0, *len));
1566 let mut out = TokenStream::new();
1567 for (record, len) in wanted {
1568 let def = self.program.types.record(record);
1569 let span = self.sp(def.range);
1570 let name = self.flexible_ident(&def.rust_name, len, span);
1571 out.extend(self.record_body(def, &name, Some(len)));
1572 }
1573 out
1574 }
1575
1576 fn record_item(&self, record: &ir::RecordDef) -> TokenStream {
1577 let span = self.sp(record.range);
1578 let name = self.c_ident(&record.rust_name, span);
1579 let item = self.record_body(record, &name, None);
1580 let accessors = self.bit_field_accessors(record, span);
1581 quote_spanned! {span=> #item #accessors }
1582 }
1583
1584 fn record_body(&self, record: &ir::RecordDef, name: &Ident, tail: Option<u64>) -> TokenStream {
1591 let span = self.sp(record.range);
1592 let derives = match (record.align, record.packed) {
1595 (_, Some(1)) => quote_spanned! {span=> #[repr(C, packed)] #[derive(Copy, Clone)] },
1599 (_, Some(pack)) => {
1600 let pack = usize_literal(pack, span);
1601 quote_spanned! {span=>
1602 #[repr(C, packed(#pack))] #[derive(Copy, Clone)]
1603 }
1604 }
1605 (Some(align), None) => {
1610 let align = usize_literal(align, span);
1611 quote_spanned! {span=>
1612 #[repr(C, align(#align))] #[derive(Copy, Clone)]
1613 }
1614 }
1615 (None, None) => quote_spanned! {span=> #[repr(C)] #[derive(Copy, Clone)] },
1616 };
1617 let byte = primitive_ty("u8", span);
1618 if !record.complete {
1619 return quote_spanned! {span=>
1622 #derives pub struct #name { _incomplete: [#byte; 0] }
1623 };
1624 }
1625 let mut fields = TokenStream::new();
1626 for rust_field in &record.rust_fields {
1627 match rust_field {
1628 ir::RustField::Member(index) => {
1629 let field = &record.fields[*index];
1630 let fspan = self.sp(field.range);
1631 let fname = self.c_ident(&field.name, fspan);
1632 let fty = match (tail, field.flexible) {
1633 (Some(len), true) => {
1634 let elem = self
1635 .program
1636 .types
1637 .elem(field.ty)
1638 .expect("a flexible member is an array");
1639 let elem = self.ty(elem, fspan);
1640 let len = usize_literal(len, fspan);
1641 bracketed(quote_spanned! {fspan=> #elem ; #len }, fspan)
1642 }
1643 _ => self.ty(field.ty, fspan),
1644 };
1645 fields.extend(quote_spanned! {fspan=> pub #fname: #fty, });
1648 }
1649 ir::RustField::Bits { name, bytes, .. } | ir::RustField::Pad { name, bytes } => {
1650 let fname = Ident::new(name, span);
1651 let len = usize_literal(*bytes, span);
1652 fields.extend(quote_spanned! {span=> pub #fname: [#byte; #len], });
1653 }
1654 ir::RustField::Align { name, align } => {
1655 let fname = Ident::new(name, span);
1656 let unit = unsigned_rust_ty((*align * 8) as u32, span);
1657 fields.extend(quote_spanned! {span=> pub #fname: [#unit; 0], });
1658 }
1659 }
1660 }
1661 if record.rust_fields.is_empty() && record.kind == RecordKind::Union {
1662 fields.extend(quote_spanned! {span=> pub __cinrs_empty: [#byte; 0], });
1666 }
1667 let body = braced(fields, span);
1668 match record.kind {
1669 RecordKind::Struct => quote_spanned! {span=> #derives pub struct #name #body },
1670 RecordKind::Union => quote_spanned! {span=> #derives pub union #name #body },
1671 }
1672 }
1673
1674 fn bit_field_accessors(&self, record: &ir::RecordDef, span: Span) -> TokenStream {
1680 let mut methods = TokenStream::new();
1681 for field in &record.fields {
1682 let Some(bits) = &field.bits else {
1683 continue;
1684 };
1685 let fspan = self.sp(field.range);
1686 methods.extend(self.bit_field_getter(record, field, bits, fspan));
1687 methods.extend(self.bit_field_setter(record, field, bits, fspan));
1688 }
1689 if methods.is_empty() {
1690 return TokenStream::new();
1691 }
1692 let name = self.c_ident(&record.rust_name, span);
1693 quote_spanned! {span=> impl #name { #methods } }
1694 }
1695
1696 fn bit_field_window(&self, bits: &ir::BitField, span: Span) -> BitWindow {
1705 let storage = Ident::new(&bits.storage, span);
1706 let start = bits.offset_in_storage();
1707 let first = start / 8;
1708 let shift = (start % 8) as u32;
1709 let count = (shift + bits.width).div_ceil(8);
1710 let word_bits: u32 = if shift + bits.width > 64 { 128 } else { 64 };
1711 let word = window_ty(word_bits, false, span);
1712 let mut read = TokenStream::new();
1713 for step in 0..count {
1714 let index = usize_literal(first + u64::from(step), span);
1715 let byte = if count == 1 {
1716 quote_spanned! {span=> self.#storage[#index] as #word }
1717 } else {
1718 quote_spanned! {span=> (self.#storage[#index] as #word) }
1719 };
1720 read.extend(if step == 0 {
1721 byte
1722 } else {
1723 let by = usize_literal(u64::from(step) * 8, span);
1724 quote_spanned! {span=> | (#byte << #by) }
1725 });
1726 }
1727 let mask = mask_of(shift + bits.width, word_bits) & !mask_of(shift, word_bits);
1731 BitWindow {
1732 read,
1733 shift,
1734 mask,
1735 word,
1736 word_bits,
1737 }
1738 }
1739
1740 fn bit_field_getter(
1741 &self,
1742 record: &ir::RecordDef,
1743 field: &ir::Field,
1744 bits: &ir::BitField,
1745 span: Span,
1746 ) -> TokenStream {
1747 let BitWindow {
1748 read,
1749 shift,
1750 word,
1751 word_bits,
1752 ..
1753 } = self.bit_field_window(bits, span);
1754 let ty = self.ty(field.ty, span);
1755 let name = self.c_ident(&bits.getter, span);
1756 let mask = word_literal(mask_of(bits.width, word_bits), word_bits, span);
1757 let shifted = if shift == 0 {
1758 quote_spanned! {span=> raw & #mask }
1759 } else {
1760 let by = usize_literal(u64::from(shift), span);
1761 quote_spanned! {span=> (raw >> #by) & #mask }
1762 };
1763 let value = if field.ty.is_bool() {
1764 quote_spanned! {span=> value != 0 }
1765 } else if !bits.signed || bits.width == word_bits {
1766 quote_spanned! {span=> value as #ty }
1767 } else {
1768 let signed = window_ty(word_bits, true, span);
1772 let by = usize_literal(u64::from(word_bits - bits.width), span);
1773 quote_spanned! {span=> (((value << #by) as #signed) >> #by) as #ty }
1774 };
1775 let body = self.accessor_body(
1776 record,
1777 quote_spanned! {span=>
1778 let raw: #word = #read;
1779 let value: #word = #shifted;
1780 #value
1781 },
1782 span,
1783 );
1784 quote_spanned! {span=>
1785 #[inline]
1786 pub fn #name(&self) -> #ty { #body }
1787 }
1788 }
1789
1790 fn bit_field_setter(
1791 &self,
1792 record: &ir::RecordDef,
1793 field: &ir::Field,
1794 bits: &ir::BitField,
1795 span: Span,
1796 ) -> TokenStream {
1797 let BitWindow {
1798 read,
1799 shift,
1800 mask,
1801 word,
1802 word_bits,
1803 } = self.bit_field_window(bits, span);
1804 let ty = self.ty(field.ty, span);
1805 let name = self.c_ident(&bits.setter, span);
1806 let storage = Ident::new(&bits.storage, span);
1807 let value = Ident::new("value", span);
1808 let field_mask = word_literal(mask, word_bits, span);
1809 let keep = word_literal(!mask & mask_of(word_bits, word_bits), word_bits, span);
1810 let shifted = if shift == 0 {
1811 quote_spanned! {span=> (#value as #word) & #field_mask }
1812 } else {
1813 let by = usize_literal(u64::from(shift), span);
1814 quote_spanned! {span=> ((#value as #word) << #by) & #field_mask }
1815 };
1816 let start = bits.offset_in_storage();
1817 let first = start / 8;
1818 let count = (shift + bits.width).div_ceil(8);
1819 let mut writes = TokenStream::new();
1820 let byte = primitive_ty("u8", span);
1821 for step in 0..count {
1822 let index = usize_literal(first + u64::from(step), span);
1823 if step == 0 {
1824 writes.extend(quote_spanned! {span=> self.#storage[#index] = raw as #byte; });
1825 } else {
1826 let by = usize_literal(u64::from(step) * 8, span);
1827 writes.extend(
1828 quote_spanned! {span=> self.#storage[#index] = (raw >> #by) as #byte; },
1829 );
1830 }
1831 }
1832 let body = self.accessor_body(
1833 record,
1834 quote_spanned! {span=>
1835 let bits: #word = #shifted;
1836 let raw: #word = #read;
1837 let raw: #word = (raw & #keep) | bits;
1838 #writes
1839 },
1840 span,
1841 );
1842 quote_spanned! {span=>
1843 #[inline]
1844 pub fn #name(&mut self, #value: #ty) { #body }
1845 }
1846 }
1847
1848 fn accessor_body(&self, record: &ir::RecordDef, body: TokenStream, span: Span) -> TokenStream {
1851 if record.kind == RecordKind::Union {
1852 let block = braced(body, span);
1853 return quote_spanned! {span=> unsafe #block };
1854 }
1855 body
1856 }
1857
1858 fn data_model_check(&self) -> TokenStream {
1878 let span = self.map.span(SourceRange::at(0));
1879 let target = &self.options.target;
1880 let chosen = format!(
1883 "Translated for {}; set CINRS_TARGET from a build script \
1884 (cargo:rustc-env=CINRS_TARGET=$TARGET) or write #pragma cinrs target.",
1885 target.describe(&self.options.target_source)
1886 );
1887 let mut body = TokenStream::new();
1888 let mut width = |ty: TokenStream, bits: u32, what: &str| {
1889 let bytes = usize_literal(u64::from(bits).div_ceil(8), span);
1890 let message = message_literal(
1891 &format!(
1892 "cinrs: {what} is {} bytes in the data model this unit was translated for, \
1893 and is not on this target. {chosen}",
1894 bits.div_ceil(8)
1895 ),
1896 span,
1897 );
1898 body.extend(quote_spanned! {span=>
1899 assert!(::core::mem::size_of::<#ty>() == #bytes, #message);
1900 });
1901 };
1902 width(
1903 quote_spanned! {span=> ::core::ffi::c_short },
1904 target.short_bits,
1905 "'short'",
1906 );
1907 width(
1908 quote_spanned! {span=> ::core::ffi::c_int },
1909 target.int_bits,
1910 "'int'",
1911 );
1912 width(
1913 quote_spanned! {span=> ::core::ffi::c_long },
1914 target.long_bits,
1915 "'long'",
1916 );
1917 width(
1918 quote_spanned! {span=> ::core::ffi::c_longlong },
1919 target.long_long_bits,
1920 "'long long'",
1921 );
1922 width(
1923 quote_spanned! {span=> *const ::core::ffi::c_void },
1924 target.ptr_bits,
1925 "a pointer",
1926 );
1927 let (test, said) = if target.char_signed {
1931 (
1932 quote_spanned! {span=> ::core::ffi::c_char::MIN != 0 },
1933 "signed",
1934 )
1935 } else {
1936 (
1937 quote_spanned! {span=> ::core::ffi::c_char::MIN == 0 },
1938 "unsigned",
1939 )
1940 };
1941 let message = message_literal(
1942 &format!(
1943 "cinrs: plain 'char' is {said} in the data model this unit was translated \
1944 for, and is not on this target. {chosen}"
1945 ),
1946 span,
1947 );
1948 body.extend(quote_spanned! {span=> assert!(#test, #message); });
1949 let align = usize_literal(target.max_scalar_align.min(8), span);
1955 let message = message_literal(
1956 &format!(
1957 "cinrs: 'long long' and 'double' are {}-byte aligned in the data model this \
1958 unit was translated for, and are not on this target — so every 'sizeof' and \
1959 member offset in it would be wrong. {chosen}",
1960 target.max_scalar_align.min(8)
1961 ),
1962 span,
1963 );
1964 body.extend(quote_spanned! {span=>
1965 assert!(
1966 ::core::mem::align_of::<::core::ffi::c_longlong>() == #align
1967 && ::core::mem::align_of::<::core::ffi::c_double>() == #align,
1968 #message
1969 );
1970 });
1971 if self.uses_int128.get() {
1972 let align = usize_literal(target.int128_align, span);
1973 let message = message_literal(
1974 &format!(
1975 "cinrs: '__int128' is {}-byte aligned in the data model this unit was \
1976 translated for, and is not on this target. {chosen}",
1977 target.int128_align
1978 ),
1979 span,
1980 );
1981 let i128 = primitive_ty("i128", span);
1982 body.extend(quote_spanned! {span=>
1983 assert!(::core::mem::align_of::<#i128>() == #align, #message);
1984 });
1985 }
1986 if self.uses_complex.get() {
1987 for (ty, name) in [
1993 (Ty::ComplexFloat, "'float _Complex'"),
1994 (Ty::ComplexDouble, "'double _Complex'"),
1995 ] {
1996 let layout = self
1997 .program
1998 .types
1999 .size_align(ty, target)
2000 .expect("a complex type has a layout");
2001 let rust = self.ty(ty, span);
2002 let size = usize_literal(layout.size, span);
2003 let align = usize_literal(layout.align, span);
2004 let message = message_literal(
2005 &format!(
2006 "cinrs: {name} is {} bytes and {}-byte aligned in the data model this \
2007 unit was translated for, and is not on this target. {chosen}",
2008 layout.size, layout.align
2009 ),
2010 span,
2011 );
2012 body.extend(quote_spanned! {span=>
2013 assert!(
2014 ::core::mem::size_of::<#rust>() == #size
2015 && ::core::mem::align_of::<#rust>() == #align,
2016 #message
2017 );
2018 });
2019 }
2020 }
2021 let block = braced(body, span);
2022 quote_spanned! {span=> const _: () = #block; }
2023 }
2024
2025 fn extern_block(&mut self) -> TokenStream {
2036 if !self.program.has_externs() {
2037 return TokenStream::new();
2038 }
2039 let span = self.map.span(SourceRange::at(0));
2040 let mut items = TokenStream::new();
2041 let mut symbols: Vec<&str> = Vec::new();
2046 for id in &self.program.externs {
2047 let object = self.program.object(*id);
2048 let Storage::Extern { item_name } = &object.storage else {
2049 continue;
2050 };
2051 let ospan = self.sp(object.range);
2052 let rust_name = self.extern_object_ident(item_name, ospan);
2053 let ty = self.ty(object.ty, ospan);
2054 let symbol = object.asm_label.as_deref().unwrap_or(item_name);
2057 symbols.push(symbol);
2058 let link = link_name(symbol, ospan);
2059 items.extend(quote_spanned! {ospan=> #link pub static mut #rust_name: #ty; });
2060 }
2061 for func in &self.program.functions {
2062 if !func.is_extern() || self.beyond_toolchain(func) {
2063 continue;
2064 }
2065 let fspan = self.sp(func.range);
2066 let rust_name = self.c_ident(func.item_name(), fspan);
2072 let params = self.extern_params(func, fspan);
2073 let ret = if func.sig.ret.is_void() {
2074 TokenStream::new()
2075 } else {
2076 let ty = self.ty(func.sig.ret, fspan);
2077 quote_spanned! {fspan=> -> #ty }
2078 };
2079 let symbol = match func.asm_label.as_deref() {
2083 Some(label) => label,
2084 None => self.msvc_symbol(&func.name),
2085 };
2086 symbols.push(symbol);
2087 let link = link_name(symbol, fspan);
2088 items.extend(quote_spanned! {fspan=> #link pub fn #rust_name(#params) #ret; });
2089 }
2090 let links = self.link_blocks(&symbols, span);
2091 quote_spanned! {span=> #links unsafe extern "C" { #items } }
2092 }
2093
2094 fn link_blocks(&self, symbols: &[&str], span: Span) -> TokenStream {
2144 let mut libraries: Vec<&str> = Vec::new();
2145 for name in &self.program.link_libraries {
2146 if !libraries.contains(&name.as_str()) {
2147 libraries.push(name);
2148 }
2149 }
2150 for library in self.legacy_stdio_libraries(symbols) {
2151 if !libraries.contains(&library) {
2152 libraries.push(library);
2153 }
2154 }
2155 let mut out = TokenStream::new();
2156 for name in libraries {
2157 let mut literal = Literal::string(name);
2158 literal.set_span(span);
2159 out.extend(quote_spanned! {span=> #[link(name = #literal)] unsafe extern "C" {} });
2160 }
2161 out
2162 }
2163
2164 fn msvc_symbol<'name>(&self, name: &'name str) -> &'name str {
2172 if !self.options.target.is_msvc() {
2173 return name;
2174 }
2175 for (c_name, symbol) in MSVC_RENAMED {
2176 if *c_name == name {
2177 return symbol;
2178 }
2179 }
2180 name
2181 }
2182
2183 fn legacy_stdio_libraries(&self, symbols: &[&str]) -> Vec<&'static str> {
2190 if !self.options.target.is_msvc() {
2191 return Vec::new();
2192 }
2193 let mut out: Vec<&'static str> = Vec::new();
2194 for (symbol, library) in LEGACY_STDIO {
2195 if symbols.contains(symbol) && !out.contains(library) {
2196 out.push(library);
2197 }
2198 }
2199 out
2200 }
2201
2202 fn extern_params(&self, func: &Function, span: Span) -> TokenStream {
2203 let mut params = TokenStream::new();
2204 for (index, ty) in func.sig.params.iter().enumerate() {
2205 if index > 0 {
2206 params.extend(quote_spanned! {span=> , });
2207 }
2208 let ty = self.ty(*ty, span);
2209 match func.param_names.get(index).and_then(|n| n.as_ref()) {
2210 Some(name) => {
2211 let name = self.c_ident(name, span);
2212 params.extend(quote_spanned! {span=> #name: #ty });
2213 }
2214 None => params.extend(quote_spanned! {span=> _: #ty }),
2215 }
2216 }
2217 if func.sig.variadic {
2218 if !func.sig.params.is_empty() {
2219 params.extend(quote_spanned! {span=> , });
2220 }
2221 params.extend(quote_spanned! {span=> ... });
2222 }
2223 params
2224 }
2225
2226 fn static_item(&mut self, var: &ir::StaticVar) -> TokenStream {
2227 let object = self.program.object(var.object);
2228 let span = self.sp(object.range);
2229 if object.storage.is_thread_local() {
2230 return self.thread_local_item(var);
2231 }
2232 let Storage::Static {
2233 item_name,
2234 exported,
2235 } = &object.storage
2236 else {
2237 return TokenStream::new();
2238 };
2239 let name = self.c_ident(item_name, span);
2240 let ty = self.binding_ty(var.object, self.storage_ty(var.object, span), span);
2241 let init = self.static_init(&var.init, object.ty, span);
2242 let init = self.binding_init(var.object, init, span);
2243 let (vis, export) = if *exported {
2244 let export = if self.program.export {
2245 let symbol = object.asm_label.as_deref().unwrap_or(&object.name);
2246 export_attr(symbol, &name, span)
2247 } else {
2248 TokenStream::new()
2249 };
2250 (quote_spanned! {span=> pub }, export)
2251 } else {
2252 (TokenStream::new(), TokenStream::new())
2253 };
2254 let section = match &object.section {
2255 Some(section) => {
2256 let mut literal = Literal::string(section);
2257 literal.set_span(span);
2258 quote_spanned! {span=> #[unsafe(link_section = #literal)] }
2259 }
2260 None => TokenStream::new(),
2261 };
2262 quote_spanned! {span=>
2266 #export
2267 #section
2268 #vis static mut #name: #ty = #init;
2269 }
2270 }
2271
2272 fn thread_local_item(&mut self, var: &ir::StaticVar) -> TokenStream {
2287 let object = self.program.object(var.object);
2288 let span = self.sp(object.range);
2289 let Storage::ThreadLocal {
2290 item_name,
2291 exported,
2292 } = &object.storage
2293 else {
2294 return TokenStream::new();
2295 };
2296 if self.program.no_std {
2297 return TokenStream::new();
2301 }
2302 let name = self.c_ident(item_name, span);
2303 let ty = self.binding_ty(var.object, self.storage_ty(var.object, span), span);
2304 let init = self.static_init(&var.init, object.ty, span);
2305 let init = self.binding_init(var.object, init, span);
2306 let vis = if *exported {
2307 quote_spanned! {span=> pub }
2308 } else {
2309 TokenStream::new()
2310 };
2311 let cell = quote_spanned! {span=> ::core::cell::UnsafeCell<#ty> };
2312 let value = quote_spanned! {span=> ::core::cell::UnsafeCell::new(#init) };
2313 let value = if self.const_initialisable(&var.init) {
2314 quote_spanned! {span=> const { #value } }
2315 } else {
2316 value
2317 };
2318 quote_spanned! {span=>
2322 ::std::thread_local! {
2323 #vis static #name: #cell = #value;
2324 }
2325 }
2326 }
2327
2328 fn const_initialisable(&self, expr: &Expr) -> bool {
2340 match &expr.kind {
2341 ExprKind::FuncAddr(_) => false,
2342 ExprKind::AddrOf(place) | ExprKind::Load(place) => match &place.kind {
2343 PlaceKind::Str(id) => {
2344 let elem = self.program.string(*id).elem;
2345 elem.size_bytes(&self.options.target) == 1
2346 }
2347 _ => !rooted_in_static(place, self.program),
2348 },
2349 ExprKind::Cast(inner) => self.const_initialisable(inner),
2350 ExprKind::PtrOffset { ptr, .. } => self.const_initialisable(ptr),
2351 ExprKind::RecordLit { fields, .. } => {
2352 fields.iter().all(|f| self.const_initialisable(f))
2353 }
2354 ExprKind::UnionLit { value, .. } => self.const_initialisable(value),
2355 ExprKind::ArrayLit(items) => items.iter().all(|i| self.const_initialisable(i)),
2356 ExprKind::ArrayRepeat { value, .. } => self.const_initialisable(value),
2357 _ => true,
2358 }
2359 }
2360
2361 fn static_init(&mut self, expr: &Expr, ty: Ty, span: Span) -> TokenStream {
2364 let tokens = self.expr_at(expr, ty);
2365 if needs_unsafe(&self.program.types, expr) {
2366 let block = braced(tokens, span);
2367 return quote_spanned! {span=> unsafe #block };
2368 }
2369 tokens
2370 }
2371
2372 fn signature(&mut self, func: &Function) -> TokenStream {
2373 let span = self.sp(func.range);
2374 let name = self.c_ident(func.item_name(), span);
2375 let mut params = TokenStream::new();
2376 for entry in &func.env {
2380 let object = self.program.object(entry.param);
2381 let pspan = self.sp(object.range);
2382 let pname = self.object_ident(entry.param, pspan);
2383 let pty = self.ty(object.ty, pspan);
2384 params.extend(quote_spanned! {pspan=> #pname: #pty , });
2385 }
2386 let named = func.params.len() == func.sig.params.len();
2390 for (index, ty) in func.sig.params.iter().enumerate() {
2391 if index > 0 {
2392 params.extend(quote_spanned! {span=> , });
2393 }
2394 if named {
2395 let id = func.params[index];
2396 let object = self.program.object(id);
2397 let pspan = self.sp(object.range);
2398 let pname = self.object_ident(id, pspan);
2399 let pty = self.ty(*ty, pspan);
2400 params.extend(quote_spanned! {pspan=> mut #pname: #pty });
2401 } else {
2402 let pname = Ident::new(&format!("__cinrs_arg{index}"), Span::mixed_site());
2403 let pty = self.ty(*ty, span);
2404 params.extend(quote_spanned! {span=> #pname: #pty });
2405 }
2406 }
2407 if func.sig.variadic {
2408 if !func.sig.params.is_empty() {
2409 params.extend(quote_spanned! {span=> , });
2410 }
2411 let name = self.va_ident();
2415 params.extend(quote_spanned! {span=> #name: ... });
2416 }
2417 let ret = if func.sig.ret.is_void() {
2418 TokenStream::new()
2419 } else {
2420 let ty = self.ty(func.sig.ret, span);
2421 quote_spanned! {span=> -> #ty }
2422 };
2423 let exported = !func.is_static && self.program.export;
2430 let vis = if func.is_static {
2431 TokenStream::new()
2432 } else {
2433 quote_spanned! {span=> pub }
2434 };
2435 let export = if exported {
2436 let symbol = func.asm_label.as_deref().unwrap_or(&func.name);
2437 export_attr(symbol, &name, span)
2438 } else {
2439 TokenStream::new()
2440 };
2441 let inline = match func.inline_hint {
2444 Some(_) if exported => TokenStream::new(),
2445 Some(ir::InlineHint::Always) => quote_spanned! {span=> #[inline(always)] },
2446 Some(ir::InlineHint::Never) => quote_spanned! {span=> #[inline(never)] },
2447 None if func.is_inline && !exported => quote_spanned! {span=> #[inline] },
2448 None => TokenStream::new(),
2449 };
2450 let cold = if func.cold {
2451 quote_spanned! {span=> #[cold] }
2452 } else {
2453 TokenStream::new()
2454 };
2455 let deprecated = match &func.deprecated {
2456 Some(Some(message)) => {
2457 let mut literal = Literal::string(message);
2458 literal.set_span(span);
2459 quote_spanned! {span=> #[deprecated(note = #literal)] }
2460 }
2461 Some(None) => quote_spanned! {span=> #[deprecated] },
2462 None => TokenStream::new(),
2463 };
2464 let section = match &func.section {
2465 Some(section) => {
2466 let mut literal = Literal::string(section);
2467 literal.set_span(span);
2468 quote_spanned! {span=> #[unsafe(link_section = #literal)] }
2469 }
2470 None => TokenStream::new(),
2471 };
2472 let unsafety = if func.is_safe() {
2477 TokenStream::new()
2478 } else {
2479 quote_spanned! {span=> unsafe }
2480 };
2481 quote_spanned! {span=>
2482 #export
2483 #inline
2484 #cold
2485 #deprecated
2486 #section
2487 #vis #unsafety extern "C" fn #name(#params) #ret
2488 }
2489 }
2490
2491 fn init_array_item(&mut self, func: &Function, kind: ir::InitKind) -> TokenStream {
2499 let span = self.sp(func.range);
2500 let name = self.c_ident(func.item_name(), span);
2501 let signature = self.function_pointer_ty(func, span);
2502 let item = Ident::new(
2503 &format!(
2504 "__CINRS_INIT_{:08x}_{}",
2505 self.program.unit_id as u32,
2506 func.item_name()
2507 ),
2508 span,
2509 );
2510 let elf = match kind {
2511 ir::InitKind::Constructor => ".init_array",
2512 ir::InitKind::Destructor => ".fini_array",
2513 };
2514 let apple = match kind {
2515 ir::InitKind::Constructor => "__DATA,__mod_init_func",
2516 ir::InitKind::Destructor => "__DATA,__mod_term_func",
2517 };
2518 let mut elf_literal = Literal::string(elf);
2519 elf_literal.set_span(span);
2520 let mut apple_literal = Literal::string(apple);
2521 apple_literal.set_span(span);
2522 quote_spanned! {span=>
2525 #[used]
2526 #[cfg_attr(target_vendor = "apple", unsafe(link_section = #apple_literal))]
2527 #[cfg_attr(not(target_vendor = "apple"), unsafe(link_section = #elf_literal))]
2528 static #item: #signature = #name;
2529 }
2530 }
2531
2532 fn init_array_guard(&self) -> TokenStream {
2539 let span = self.map.span(SourceRange::at(0));
2540 quote_spanned! {span=>
2541 const _: () = {
2542 #[cfg(not(any(target_os = "linux", target_os = "android",
2543 target_os = "freebsd", target_os = "netbsd",
2544 target_os = "openbsd", target_os = "dragonfly",
2545 target_vendor = "apple")))]
2546 ::core::compile_error!(
2547 "'constructor' and 'destructor' need a target whose runtime walks an initialiser table (ELF or Mach-O)"
2548 );
2549 };
2550 }
2551 }
2552
2553 fn function_item(&mut self, func: &Function) -> TokenStream {
2554 let span = self.sp(func.range);
2555 let Some(body) = &func.body else {
2556 return TokenStream::new();
2557 };
2558 self.enter_function(func);
2559 let signature = self.signature(func);
2560 let body = match body {
2561 Body::Structured(stmts) => self.stmts(stmts),
2562 Body::Cfg(cfg) => self.cfg_body(cfg, span),
2563 };
2564 let arena = if func.uses_alloca {
2568 self.alloca_arena(span)
2569 } else {
2570 TokenStream::new()
2571 };
2572 if func.is_safe() {
2577 return quote_spanned! {span=>
2578 #signature { #arena #body }
2579 };
2580 }
2581 quote_spanned! {span=>
2582 #signature {
2583 unsafe { #arena #body }
2584 }
2585 }
2586 }
2587
2588 fn alloca_arena(&self, span: Span) -> TokenStream {
2595 let name = self.alloca_ident();
2596 let block = self.vec_ty(primitive_ty("u128", span), span);
2597 let arena = self.vec_ty(block, span);
2598 let empty = self.vec_new(span);
2599 quote_spanned! {span=> let mut #name: #arena = #empty; }
2600 }
2601
2602 fn alloca_ident(&self) -> Ident {
2604 Ident::new("__cinrs_alloca", Span::mixed_site())
2605 }
2606
2607 fn stub_item(&mut self, func: &Function) -> TokenStream {
2608 let span = self.sp(func.range);
2609 self.enter_function(func);
2610 let signature = self.signature(func);
2611 if func.is_safe() {
2612 return quote_spanned! {span=>
2613 #signature { ::core::unreachable!() }
2614 };
2615 }
2616 quote_spanned! {span=>
2617 #signature {
2618 unsafe { ::core::unreachable!() }
2619 }
2620 }
2621 }
2622
2623 fn enter_function(&mut self, func: &Function) {
2625 self.ret_ty = func.sig.ret;
2626 self.in_cfg = matches!(func.body, Some(Body::Cfg(_)));
2627 self.temporaries = 0;
2628 self.continue_styles.clear();
2629 self.local_names.clear();
2630 self.region_names.clear();
2631 self.region_kinds.clear();
2632 if let Some(Body::Structured(stmts)) = &func.body {
2633 self.name_regions(stmts);
2634 }
2635 self.env = func
2636 .env
2637 .iter()
2638 .map(|entry| (entry.owner, entry.param))
2639 .collect();
2640
2641 let mut bound: Vec<ir::ObjectId> = func.env.iter().map(|entry| entry.param).collect();
2647 bound.extend(func.params.iter().copied());
2648 if let Some(Body::Cfg(cfg)) = &func.body {
2649 for local in &cfg.locals {
2650 self.local_names
2651 .insert(local.object, local.rust_name.clone());
2652 }
2653 }
2654 bound.extend(func.locals.iter().copied());
2655 self.rename_shadowing(&bound);
2656
2657 self.va_source = if func.sig.variadic {
2658 VaSource::Ellipsis
2659 } else {
2660 func.params
2664 .iter()
2665 .find_map(|id| {
2666 let ty = self.program.object(*id).ty;
2667 if ty.is_va_list() {
2668 return Some(VaSource::Param(*id));
2669 }
2670 let pointee = self.program.types.pointee(ty)?;
2671 pointee.is_va_list().then_some(VaSource::PtrParam(*id))
2672 })
2673 .unwrap_or(VaSource::None)
2674 };
2675 }
2676
2677 fn plain_local_name(&self, id: ir::ObjectId) -> String {
2680 match self.local_names.get(&id) {
2681 Some(name) => name.clone(),
2682 None => self.program.object(id).name.clone(),
2683 }
2684 }
2685
2686 fn rename_shadowing(&mut self, bound: &[ir::ObjectId]) {
2696 if self.reserved.is_empty() {
2697 return;
2698 }
2699 let mut used: HashSet<String> = bound
2700 .iter()
2701 .map(|id| {
2702 self.names
2703 .spelling(&self.plain_local_name(*id))
2704 .into_owned()
2705 })
2706 .collect();
2707 for id in bound {
2708 let base = self.plain_local_name(*id);
2709 if !self.reserved.contains(&base) {
2710 continue;
2711 }
2712 let name = (1u32..)
2713 .map(|n| format!("{base}_{n}"))
2714 .find(|c| {
2715 !used.contains(self.names.spelling(c).as_ref()) && !self.reserved.contains(c)
2716 })
2717 .expect("the sequence of candidates is unbounded");
2718 used.insert(self.names.spelling(&name).into_owned());
2719 self.local_names.insert(*id, name);
2720 }
2721 }
2722
2723 fn va_ident(&self) -> Ident {
2728 Ident::new("__cinrs_va", Span::mixed_site())
2729 }
2730
2731 fn va_pristine(&mut self, span: Span) -> TokenStream {
2733 match self.va_source {
2734 VaSource::Param(id) => {
2735 let name = self.object_ident(id, span);
2736 quote_spanned! {span=> #name.clone() }
2737 }
2738 VaSource::PtrParam(id) => {
2739 let name = self.object_ident(id, span);
2740 quote_spanned! {span=> (*#name).clone() }
2741 }
2742 _ => {
2745 let name = self.va_ident();
2746 quote_spanned! {span=> #name.clone() }
2747 }
2748 }
2749 }
2750
2751 fn object_align(&self, id: ir::ObjectId) -> Option<u64> {
2758 self.program.object(id).align
2759 }
2760
2761 fn binding_ty(&self, id: ir::ObjectId, ty: TokenStream, span: Span) -> TokenStream {
2763 match self.object_align(id) {
2764 Some(align) => {
2765 let wrapper = align_wrapper_ident(align, span);
2766 quote_spanned! {span=> #wrapper<#ty> }
2767 }
2768 None => ty,
2769 }
2770 }
2771
2772 fn binding_init(&self, id: ir::ObjectId, init: TokenStream, span: Span) -> TokenStream {
2774 match self.object_align(id) {
2775 Some(align) => {
2776 let wrapper = align_wrapper_ident(align, span);
2777 quote_spanned! {span=> #wrapper(#init) }
2778 }
2779 None => init,
2780 }
2781 }
2782
2783 fn storage_ty(&self, id: ir::ObjectId, span: Span) -> TokenStream {
2787 let object = self.program.object(id);
2788 match (object.flexible_len, object.ty) {
2789 (Some(len), Ty::Record(record)) => {
2790 let name =
2791 self.flexible_ident(&self.program.types.record(record).rust_name, len, span);
2792 quote_spanned! {span=> #name }
2793 }
2794 _ => self.ty(object.ty, span),
2795 }
2796 }
2797
2798 fn object_access(&self, id: ir::ObjectId, span: Span) -> TokenStream {
2801 let name = self.object_ident(id, span);
2802 self.through_storage(id, quote_spanned! {span=> #name }, span)
2803 }
2804
2805 fn through_storage(&self, id: ir::ObjectId, base: TokenStream, span: Span) -> TokenStream {
2812 let object = self.program.object(id);
2813 let mut access = base;
2814 if object.align.is_some() {
2815 let field = Literal::usize_unsuffixed(0);
2816 access = quote_spanned! {span=> #access.#field };
2817 }
2818 if object.flexible_len.is_some() {
2819 let ty = self.ty(object.ty, span);
2820 access = parenthesize(
2821 quote_spanned! {span=> *(&raw mut #access).cast::<#ty>() },
2822 span,
2823 );
2824 }
2825 access
2826 }
2827
2828 #[inline(never)]
2836 fn va_list_load(&mut self, place: &Place, span: Span) -> Value {
2837 let lowered = self.place(place, false);
2838 let access = lowered.access;
2839 let value = Value::new(quote_spanned! {span=> #access.clone() }, prec::CALL);
2840 if lowered.setup.is_empty() {
2841 return value;
2842 }
2843 let setup = lowered.setup;
2844 let tokens = value.at(prec::LOWEST, span);
2845 Value::new(quote_spanned! {span=> { #setup #tokens } }, prec::BLOCK)
2846 }
2847
2848 #[inline(never)]
2856 fn label_address(&self, id: ir::LabelId, ty: Ty, span: Span) -> Value {
2857 let state = self.label_states.get(&id).copied().unwrap_or(0);
2858 let mut literal = Literal::usize_suffixed(state as usize);
2859 literal.set_span(span);
2860 let target = self.ty(ty, span);
2861 Value::new(quote_spanned! {span=> #literal as #target }, prec::CAST).type_end(true)
2862 }
2863
2864 #[inline(never)]
2866 fn label_difference(&self, lhs: &Expr, rhs: &Expr, ty: Ty, span: Span) -> Value {
2867 let left = self.label_state_literal(lhs, span);
2868 let right = self.label_state_literal(rhs, span);
2869 let target = self.ty(ty, span);
2870 Value::new(
2871 quote_spanned! {span=> (#left - #right) as #target },
2872 prec::CAST,
2873 )
2874 .type_end(true)
2875 }
2876
2877 fn label_state_literal(&self, expr: &Expr, span: Span) -> TokenStream {
2880 let id = label_state(expr).expect("a label address");
2881 let state = self.label_states.get(&id).copied().unwrap_or(0);
2882 let mut literal = Literal::isize_suffixed(state as isize);
2883 literal.set_span(span);
2884 quote_spanned! {span=> #literal }
2885 }
2886
2887 fn object_ident(&self, id: ir::ObjectId, span: Span) -> Ident {
2889 let object = self.program.object(id);
2890 match &object.storage {
2891 Storage::Automatic => match self.local_names.get(&id) {
2892 Some(name) => self.c_ident(name, span),
2893 None => self.c_ident(&object.name, span),
2894 },
2895 Storage::Static { item_name, .. } | Storage::ThreadLocal { item_name, .. } => {
2898 self.c_ident(item_name, span)
2899 }
2900 Storage::Extern { item_name } => self.extern_object_ident(item_name, span),
2901 }
2902 }
2903
2904 fn cfg_body(&mut self, cfg: &Cfg, span: Span) -> TokenStream {
2911 let mut out = TokenStream::new();
2912 for local in &cfg.locals {
2913 let object = self.program.object(local.object);
2914 let ospan = self.sp(object.range);
2915 let name = self.object_ident(local.object, ospan);
2916 let (ty, init) = if object.vla_storage {
2920 let elem = self.ty(object.ty, ospan);
2921 (self.vec_ty(elem, ospan), self.vec_new(ospan))
2922 } else if object.ty.is_va_list() {
2923 (self.ty(object.ty, ospan), self.va_pristine(ospan))
2927 } else {
2928 (
2929 self.ty(object.ty, ospan),
2930 self.zero_tokens(object.ty, ospan),
2931 )
2932 };
2933 let ty = self.binding_ty(local.object, ty, ospan);
2934 let init = self.binding_init(local.object, init, ospan);
2935 out.extend(quote_spanned! {ospan=> let mut #name: #ty = #init; });
2936 }
2937 let state = self.state_ident();
2938 let label = self.cfg_label();
2939 let mut arms = TokenStream::new();
2940 for (index, block) in cfg.blocks.iter().enumerate() {
2941 let bspan = self.block_span(block, span);
2942 let pattern = state_literal(index, bspan);
2943 let body = self.block_tokens(block, bspan);
2944 arms.extend(quote_spanned! {bspan=> #pattern => { #body } });
2945 }
2946 arms.extend(quote_spanned! {span=> _ => ::core::unreachable!(), });
2947 let u32_ty = primitive_ty("u32", span);
2948 quote_spanned! {span=>
2949 #out
2950 let mut #state: #u32_ty = 0;
2951 #label: loop {
2952 match #state { #arms }
2953 }
2954 }
2955 }
2956
2957 fn state_ident(&self) -> Ident {
2959 Ident::new("__cinrs_state", Span::mixed_site())
2960 }
2961
2962 fn cfg_label(&self) -> TokenStream {
2963 self.label("cfg", Span::mixed_site())
2964 }
2965
2966 fn block_span(&self, block: &BasicBlock, fallback: Span) -> Span {
2969 if let Some(range) = block.stmts.iter().find_map(|s| self.stmt_range(s)) {
2970 return self.sp(range);
2971 }
2972 match &block.term {
2973 Terminator::Jump { range, .. }
2974 | Terminator::Switch { range, .. }
2975 | Terminator::IndirectJump { range, .. }
2976 | Terminator::Return { range, .. } => self.sp(*range),
2977 Terminator::Branch { cond, .. } => self.sp(cond.range),
2978 Terminator::Unreachable => fallback,
2979 }
2980 }
2981
2982 fn block_tokens(&mut self, block: &BasicBlock, span: Span) -> TokenStream {
2983 let mut out = self.stmts(&block.stmts);
2984 let label = self.cfg_label();
2985 match &block.term {
2986 Terminator::Jump { target, range } => {
2987 let jump = self.enter_block(*target, self.sp(*range));
2988 out.extend(quote_spanned! {span=> #jump continue #label; });
2989 }
2990 Terminator::IndirectJump { target, range, .. } => {
2995 let gspan = self.sp(*range);
2996 let state = self.state_ident();
2997 let pointer = self.expr(target).at(prec::CAST, gspan);
2998 let usize_ty = primitive_ty("usize", gspan);
2999 let u32_ty = primitive_ty("u32", gspan);
3000 out.extend(quote_spanned! {gspan=>
3001 #state = #pointer as #usize_ty as #u32_ty;
3002 continue #label;
3003 });
3004 }
3005 Terminator::Branch {
3006 cond,
3007 then_blk,
3008 else_blk,
3009 } => {
3010 let cspan = self.sp(cond.range);
3011 let test = self.condition(cond).at_condition(cspan);
3012 let then_tokens = self.enter_block(*then_blk, cspan);
3013 let else_tokens = self.enter_block(*else_blk, cspan);
3014 out.extend(quote_spanned! {cspan=>
3015 if #test { #then_tokens } else { #else_tokens }
3016 continue #label;
3017 });
3018 }
3019 Terminator::Switch {
3020 value,
3021 cases,
3022 default,
3023 range,
3024 } => {
3025 let sspan = self.sp(*range);
3026 let scrutinee = self.expr(value).at(prec::UNARY, sspan);
3027 let mut arms = TokenStream::new();
3028 for (target, values) in group_cases(cases) {
3029 let mut pattern = TokenStream::new();
3030 for (index, case) in values.iter().enumerate() {
3031 if index > 0 {
3032 pattern.extend(quote_spanned! {sspan=> | });
3033 }
3034 pattern.extend(case_pattern(*case, value.ty, sspan));
3035 }
3036 let enter = self.enter_block(target, sspan);
3037 arms.extend(quote_spanned! {sspan=> #pattern => { #enter } });
3038 }
3039 let enter = self.enter_block(*default, sspan);
3040 arms.extend(quote_spanned! {sspan=> _ => { #enter } });
3041 out.extend(quote_spanned! {sspan=>
3042 match #scrutinee { #arms }
3043 continue #label;
3044 });
3045 }
3046 Terminator::Return { value, range } => {
3047 let rspan = self.sp(*range);
3048 match value {
3049 Some(value) => {
3050 let ret = self.ret_ty;
3051 let tokens = self.expr_at(value, ret);
3052 out.extend(quote_spanned! {rspan=> return #tokens; });
3053 }
3054 None => out.extend(quote_spanned! {rspan=> return; }),
3055 }
3056 }
3057 Terminator::Unreachable => {
3058 out.extend(quote_spanned! {span=> ::core::unreachable!(); });
3059 }
3060 }
3061 out
3062 }
3063
3064 fn enter_block(&self, target: BlockId, span: Span) -> TokenStream {
3066 let state = self.state_ident();
3067 let value = state_literal(target.0 as usize, span);
3068 quote_spanned! {span=> #state = #value; }
3069 }
3070
3071 fn stmts(&mut self, stmts: &[Stmt]) -> TokenStream {
3074 let mut out = TokenStream::new();
3075 for stmt in stmts {
3076 out.extend(self.stmt(stmt));
3077 }
3078 out
3079 }
3080
3081 fn block_of(&mut self, stmt: &Stmt, span: Span) -> TokenStream {
3084 match stmt {
3085 Stmt::Block(items) => {
3086 let items = self.stmts(items);
3087 braced(items, span)
3088 }
3089 other => {
3090 let tokens = self.stmt(other);
3091 braced(tokens, span)
3092 }
3093 }
3094 }
3095
3096 fn stmt(&mut self, stmt: &Stmt) -> TokenStream {
3097 match stmt {
3098 Stmt::Nop => TokenStream::new(),
3099 Stmt::Expr(expr) => self.expr_stmt(expr),
3100 Stmt::Let { object, init, .. } => {
3101 let id = *object;
3102 let name = self.object_ident(id, self.sp(self.program.object(id).range));
3103 let object = self.program.object(id);
3104 let span = self.sp(object.range);
3105 let object_ty = object.ty;
3106 let ty = self.binding_ty(id, self.ty(object_ty, span), span);
3107 let init = self.expr_at(init, object_ty);
3108 let init = self.binding_init(id, init, span);
3109 quote_spanned! {span=> let mut #name: #ty = #init; }
3110 }
3111 Stmt::Vla(def) => self.vla_def(def),
3112 Stmt::Cleanup(def) => self.cleanup_def(def),
3113 Stmt::Block(items) => {
3114 let span = self.stmts_span(items);
3115 let items = self.stmts(items);
3116 braced(items, span)
3117 }
3118 Stmt::If {
3119 cond,
3120 then_branch,
3121 else_branch,
3122 } => {
3123 let span = self.sp(cond.range);
3124 let cond_tokens = self.condition(cond).at_condition(span);
3125 let then_tokens = self.block_of(then_branch, span);
3126 let else_tokens = match else_branch {
3127 Some(branch) => {
3128 let tokens = self.block_of(branch, span);
3129 quote_spanned! {span=> else #tokens }
3130 }
3131 None => TokenStream::new(),
3132 };
3133 quote_spanned! {span=> if #cond_tokens #then_tokens #else_tokens }
3134 }
3135 Stmt::While {
3136 id,
3137 cond,
3138 body,
3139 range,
3140 } => {
3141 let span = self.sp(*range);
3142 self.continue_styles.insert(*id, ContinueStyle::Head);
3143 let label = self.loop_label(*id, span);
3144 let body_tokens = self.block_of(body, span);
3145 if ir::is_always_true(cond) {
3149 return quote_spanned! {span=> #label: loop #body_tokens };
3150 }
3151 let cond_tokens = self.condition(cond).at_condition(span);
3152 quote_spanned! {span=> #label: while #cond_tokens #body_tokens }
3153 }
3154 Stmt::DoWhile {
3155 id,
3156 body,
3157 cond,
3158 range,
3159 } => {
3160 let span = self.sp(*range);
3161 self.continue_styles.insert(*id, ContinueStyle::BodyLabel);
3162 let label = self.loop_label(*id, span);
3163 let body_label = self.loop_body_label(*id, span);
3164 let body_tokens = self.block_of(body, span);
3165 let test = if ir::is_always_true(cond) {
3166 TokenStream::new()
3167 } else {
3168 let cond_tokens = self.condition(cond).at(prec::LOWEST, span);
3169 quote_spanned! {span=> if !(#cond_tokens) { break #label; } }
3170 };
3171 quote_spanned! {span=>
3172 #label: loop {
3173 #body_label: #body_tokens
3174 #test
3175 }
3176 }
3177 }
3178 Stmt::For {
3179 id,
3180 init,
3181 cond,
3182 step,
3183 body,
3184 range,
3185 } => {
3186 let span = self.sp(*range);
3187 self.continue_styles.insert(*id, ContinueStyle::BodyLabel);
3188 let label = self.loop_label(*id, span);
3189 let body_label = self.loop_body_label(*id, span);
3190 let init_tokens = self.stmts(init);
3191 let test = match cond {
3192 Some(cond) if !ir::is_always_true(cond) => {
3193 let tokens = self.condition(cond).at(prec::LOWEST, span);
3194 quote_spanned! {span=> if !(#tokens) { break #label; } }
3195 }
3196 _ => TokenStream::new(),
3197 };
3198 let body_tokens = self.block_of(body, span);
3199 let step_tokens = match step {
3200 Some(step) => self.expr_stmt(step),
3201 None => TokenStream::new(),
3202 };
3203 quote_spanned! {span=>
3206 {
3207 #init_tokens
3208 #label: loop {
3209 #test
3210 #body_label: #body_tokens
3211 #step_tokens
3212 }
3213 }
3214 }
3215 }
3216 Stmt::Switch(switch) => self.switch(switch),
3217 Stmt::Region(region) => self.region(region),
3218 Stmt::Label { body, .. } | Stmt::Case { body, .. } => self.stmt(body),
3222 Stmt::Goto { id, range } => {
3226 let span = self.sp(*range);
3227 match self.region_kinds.get(id).copied() {
3228 Some(kind) => {
3229 let label = self.region_label(*id, span);
3230 match kind {
3231 ir::RegionKind::Block => quote_spanned! {span=> break #label; },
3232 ir::RegionKind::Loop => quote_spanned! {span=> continue #label; },
3233 }
3234 }
3235 None => TokenStream::new(),
3236 }
3237 }
3238 Stmt::GotoPtr { .. } => TokenStream::new(),
3239 Stmt::SwitchTree(switch) => self.stmt(&switch.body),
3240 Stmt::Break { target, range } => {
3241 let span = self.sp(*range);
3242 let label = match target {
3243 BreakTarget::Loop(id) => self.loop_label(*id, span),
3244 BreakTarget::Switch(id) => self.switch_label(*id, span),
3245 };
3246 quote_spanned! {span=> break #label; }
3247 }
3248 Stmt::Continue { id, range } => {
3249 let span = self.sp(*range);
3250 match self.continue_styles.get(id) {
3251 Some(ContinueStyle::BodyLabel) => {
3252 let label = self.loop_body_label(*id, span);
3253 quote_spanned! {span=> break #label; }
3254 }
3255 _ => {
3256 let label = self.loop_label(*id, span);
3257 quote_spanned! {span=> continue #label; }
3258 }
3259 }
3260 }
3261 Stmt::Return { value, range } => {
3262 let span = self.sp(*range);
3263 match value {
3264 Some(value) => {
3265 let ret = self.ret_ty;
3266 let tokens = self.expr_at(value, ret);
3267 quote_spanned! {span=> return #tokens; }
3268 }
3269 None => quote_spanned! {span=> return; },
3270 }
3271 }
3272 }
3273 }
3274
3275 fn vla_def(&mut self, def: &ir::VlaDef) -> TokenStream {
3284 let span = self.sp(def.range);
3285 let object = self.program.object(def.object);
3286 let elem = self.program.types.vm_step_ty(object.ty);
3289 let store = self.object_ident(def.storage, span);
3290 let name = self.object_ident(def.object, span);
3291 let count = self.expr(&def.count).at(prec::CAST, span);
3292 let zero = self.zero_tokens(elem, span);
3293 let elem_ty = self.ty(elem, span);
3294 let usize_ty = primitive_ty("usize", span);
3295 let elements = self.vec_of(zero, quote_spanned! {span=> #count as #usize_ty }, span);
3296 if self.in_cfg {
3297 return quote_spanned! {span=>
3298 #store = #elements;
3299 #name = #store.as_mut_ptr();
3300 };
3301 }
3302 let vec_ty = self.vec_ty(elem_ty.clone(), span);
3303 quote_spanned! {span=>
3304 let mut #store: #vec_ty = #elements;
3305 let mut #name: *mut #elem_ty = #store.as_mut_ptr();
3306 }
3307 }
3308
3309 fn cleanup_def(&mut self, def: &ir::CleanupDef) -> TokenStream {
3315 let span = self.sp(def.range);
3316 let guard = cleanup_guard_ty();
3317 let name = Ident::new(
3320 &format!("__cinrs_cleanup{}", def.object.0),
3321 Span::mixed_site(),
3322 );
3323 let object = self.program.object(def.object);
3324 let place = ir::Place {
3325 kind: PlaceKind::Object(def.object),
3326 ty: object.ty,
3327 is_const: object.is_const,
3328 range: def.range,
3329 };
3330 let address = self
3331 .address_of(&place, def.param, span)
3332 .at(prec::LOWEST, span);
3333 let function = self.func_pointer(def.func, span);
3334 self.uses_cleanup.set(true);
3335 quote_spanned! {span=>
3336 let #name = #guard(#address, #function);
3337 }
3338 }
3339
3340 fn cleanup_guard_item(&self, span: Span) -> TokenStream {
3350 let name = cleanup_guard_ty();
3351 let p = Ident::new("P", Span::mixed_site());
3352 let r = Ident::new("R", Span::mixed_site());
3353 quote_spanned! {span=>
3354 struct #name<#p: ::core::marker::Copy, #r>(#p, unsafe extern "C" fn(#p) -> #r);
3355 impl<#p: ::core::marker::Copy, #r> ::core::ops::Drop for #name<#p, #r> {
3356 fn drop(&mut self) {
3357 unsafe {
3358 (self.1)(self.0);
3359 }
3360 }
3361 }
3362 }
3363 }
3364
3365 fn func_pointer(&self, id: ir::FuncId, span: Span) -> TokenStream {
3367 let function = self.program.function(id);
3368 let name = self.function_path(function, span);
3369 let signature = self.function_pointer_ty(function, span);
3370 quote_spanned! {span=> #name as #signature }
3371 }
3372
3373 fn stmts_span(&self, stmts: &[Stmt]) -> Span {
3375 stmts
3376 .iter()
3377 .find_map(|s| self.stmt_range(s))
3378 .map_or_else(Span::call_site, |range| self.sp(range))
3379 }
3380
3381 fn stmt_range(&self, stmt: &Stmt) -> Option<SourceRange> {
3382 Some(match stmt {
3383 Stmt::Expr(expr) => expr.range,
3384 Stmt::Let { object, .. } => self.program.object(*object).range,
3385 Stmt::Vla(def) => def.range,
3386 Stmt::Cleanup(def) => def.range,
3387 Stmt::If { cond, .. } => cond.range,
3388 Stmt::While { range, .. }
3389 | Stmt::DoWhile { range, .. }
3390 | Stmt::For { range, .. }
3391 | Stmt::Break { range, .. }
3392 | Stmt::Continue { range, .. }
3393 | Stmt::Return { range, .. }
3394 | Stmt::Label { range, .. }
3395 | Stmt::Case { range, .. }
3396 | Stmt::Goto { range, .. }
3397 | Stmt::GotoPtr { range, .. } => *range,
3398 Stmt::Switch(switch) => switch.range,
3399 Stmt::SwitchTree(switch) => switch.range,
3400 Stmt::Region(region) => region.range,
3401 Stmt::Block(items) => return items.iter().find_map(|s| self.stmt_range(s)),
3402 Stmt::Nop => return None,
3403 })
3404 }
3405
3406 fn loop_label(&self, id: LoopId, span: Span) -> TokenStream {
3407 self.label(&format!("l{}", id.0), span)
3408 }
3409
3410 fn loop_body_label(&self, id: LoopId, span: Span) -> TokenStream {
3411 self.label(&format!("l{}_body", id.0), span)
3412 }
3413
3414 fn switch_label(&self, id: ir::SwitchId, span: Span) -> TokenStream {
3415 self.label(&format!("sw{}", id.0), span)
3416 }
3417
3418 fn switch_case_label(&self, id: ir::SwitchId, index: usize, span: Span) -> TokenStream {
3419 self.label(&format!("sw{}_c{index}", id.0), span)
3420 }
3421
3422 fn region_label(&self, id: ir::LabelId, span: Span) -> TokenStream {
3424 match self.region_names.get(&id) {
3425 Some(name) => self.label(name, span),
3426 None => self.label(&format!("cinrs_label{}", id.0), span),
3429 }
3430 }
3431
3432 fn region(&mut self, region: &ir::Region) -> TokenStream {
3440 let span = self.sp(region.range);
3441 let label = self.region_label(region.label, span);
3442 let previous = self.region_kinds.insert(region.label, region.kind);
3443 let body = self.stmts(®ion.body);
3444 match previous {
3445 Some(kind) => self.region_kinds.insert(region.label, kind),
3446 None => self.region_kinds.remove(®ion.label),
3447 };
3448 match region.kind {
3449 ir::RegionKind::Block => quote_spanned! {span=> #label: { #body } },
3450 ir::RegionKind::Loop if region.falls_out => quote_spanned! {span=>
3451 #label: loop { #body break #label; }
3452 },
3453 ir::RegionKind::Loop => quote_spanned! {span=> #label: loop { #body } },
3454 }
3455 }
3456
3457 fn name_regions(&mut self, stmts: &[Stmt]) {
3467 let mut taken: HashSet<String> = HashSet::new();
3468 let mut found = Vec::new();
3469 collect_regions(stmts, &mut found);
3470 for (id, name) in found {
3471 if self.region_names.contains_key(&id) {
3472 continue;
3473 }
3474 let mut candidate = rust_spelling(&name).into_owned();
3475 if !is_label_name(&candidate) {
3476 candidate = format!("{candidate}_{}", id.0);
3477 }
3478 if !is_label_name(&candidate) {
3479 candidate = format!("cinrs_label{}", id.0);
3480 }
3481 while taken.contains(&candidate) {
3482 candidate.push('_');
3483 }
3484 taken.insert(candidate.clone());
3485 self.region_names.insert(id, candidate);
3486 }
3487 }
3488
3489 fn switch(&mut self, switch: &Switch) -> TokenStream {
3491 let span = self.sp(switch.range);
3492 let scrutinee_ty = switch.scrutinee.ty;
3493 let scrutinee = self.expr(&switch.scrutinee).at(prec::UNARY, span);
3494
3495 let mut arms = TokenStream::new();
3496 for (index, group) in switch.groups.iter().enumerate() {
3497 if group.values.is_empty() {
3498 continue;
3499 }
3500 let label = self.switch_case_label(switch.id, index, span);
3501 let mut pattern = TokenStream::new();
3502 for (i, value) in group.values.iter().enumerate() {
3503 if i > 0 {
3504 pattern.extend(quote_spanned! {span=> | });
3505 }
3506 pattern.extend(case_pattern(*value, scrutinee_ty, span));
3509 }
3510 arms.extend(quote_spanned! {span=> #pattern => break #label, });
3511 }
3512 let fallback = match switch.default_group {
3513 Some(index) => self.switch_case_label(switch.id, index, span),
3514 None => self.switch_label(switch.id, span),
3515 };
3516 arms.extend(quote_spanned! {span=> _ => break #fallback, });
3517
3518 let prelude = self.stmts(&switch.prelude);
3521 let mut inner = quote_spanned! {span=> match #scrutinee { #arms } #prelude };
3522 for (index, group) in switch.groups.iter().enumerate() {
3523 let label = self.switch_case_label(switch.id, index, span);
3524 let body = self.stmts(&group.body);
3525 inner = quote_spanned! {span=> #label: { #inner } #body };
3526 }
3527
3528 let mut hoisted = TokenStream::new();
3529 for id in &switch.hoisted {
3530 let object = self.program.object(*id);
3531 let ospan = self.sp(object.range);
3532 let name = self.object_ident(*id, ospan);
3533 let ty = self.binding_ty(*id, self.ty(object.ty, ospan), ospan);
3534 let zero = self.zero_tokens(object.ty, ospan);
3535 let zero = self.binding_init(*id, zero, ospan);
3536 hoisted.extend(quote_spanned! {ospan=> let mut #name: #ty = #zero; });
3537 }
3538
3539 let label = self.switch_label(switch.id, span);
3540 quote_spanned! {span=>
3543 {
3544 #hoisted
3545 #label: { #inner }
3546 }
3547 }
3548 }
3549
3550 fn expr_stmt(&mut self, expr: &Expr) -> TokenStream {
3552 let span = self.sp(expr.range);
3553 match &expr.kind {
3554 ExprKind::Assign { place, value } => {
3555 let lowered = self.place(place, true);
3556 let value = self.expr_at(value, self.program.types.unatomic(place.ty));
3557 let store = self.write(&lowered, value, span);
3558 let setup = &lowered.setup;
3559 quote_spanned! {span=> #setup #store }
3560 }
3561 ExprKind::CompoundAssign {
3562 place,
3563 op,
3564 value,
3565 compute,
3566 } => {
3567 let lowered = self.place(place, true);
3568 if lowered.atomic.is_some() {
3569 let kind = PlaceRmw::Compound {
3570 op: *op,
3571 value,
3572 compute: *compute,
3573 };
3574 return self.atomic_place_rmw(&lowered, kind, RmwValue::None, span);
3575 }
3576 let (hoist, rhs) = self.compound_rhs(value);
3577 let current = self.read(&lowered, span);
3578 let updated = self.compound_value(current, place.ty, *op, value, rhs, *compute);
3579 let updated = updated.at(prec::LOWEST, span);
3580 let store = self.write(&lowered, updated, span);
3581 let setup = &lowered.setup;
3582 quote_spanned! {span=> #setup #hoist #store }
3583 }
3584 ExprKind::IncDec { place, dec, .. } => {
3585 let lowered = self.place(place, true);
3586 if lowered.atomic.is_some() {
3587 let kind = PlaceRmw::Step { dec: *dec };
3588 return self.atomic_place_rmw(&lowered, kind, RmwValue::None, span);
3589 }
3590 let current = self.read(&lowered, span);
3591 let next = self.step_value(current, place.ty, *dec, span);
3592 let store = self.write(&lowered, next, span);
3593 let setup = &lowered.setup;
3594 quote_spanned! {span=> #setup #store }
3595 }
3596 ExprKind::Call { .. } => {
3597 let diverges = ir::expr_never_returns(expr, &self.program.functions);
3605 let tokens = self.expr(expr).at(prec::LOWEST, span);
3606 if diverges {
3607 quote_spanned! {span=> #tokens; ::core::unreachable!(); }
3608 } else {
3609 quote_spanned! {span=> #tokens; }
3610 }
3611 }
3612 ExprKind::Unreachable => {
3613 quote_spanned! {span=> ::core::hint::unreachable_unchecked(); }
3614 }
3615 ExprKind::Atomic(_) if expr.ty.is_void() => {
3619 let tokens = self.expr(expr).at(prec::LOWEST, span);
3620 quote_spanned! {span=> #tokens; }
3621 }
3622 ExprKind::Comma { .. } => {
3623 let mut out = TokenStream::new();
3628 for operand in comma_operands(expr) {
3629 out.extend(self.expr_stmt(operand));
3630 }
3631 out
3632 }
3633 ExprKind::Cond { .. } => {
3634 let mut spine = Vec::new();
3637 let mut node = expr;
3638 while let ExprKind::Cond {
3639 cond,
3640 then_expr,
3641 else_expr,
3642 } = &node.kind
3643 {
3644 let span = self.sp(node.range);
3645 let cond_tokens = self.condition(cond).at_condition(span);
3646 let then_tokens = self.expr_stmt(then_expr);
3647 spine.push((cond_tokens, then_tokens, span));
3648 node = else_expr;
3649 }
3650 let mut tokens = self.expr_stmt(node);
3651 while let Some((cond_tokens, then_tokens, span)) = spine.pop() {
3652 tokens = quote_spanned! {span=>
3653 if #cond_tokens { #then_tokens } else { #tokens }
3654 };
3655 }
3656 tokens
3657 }
3658 ExprKind::Cast(inner) if expr.ty.is_void() => self.expr_stmt(inner),
3661 ExprKind::VaEnd => TokenStream::new(),
3663 _ => {
3664 let tokens = self.expr(expr).at(prec::LOWEST, span);
3665 quote_spanned! {span=> let _ = #tokens; }
3666 }
3667 }
3668 }
3669
3670 fn expr_at(&mut self, expr: &Expr, expected: Ty) -> TokenStream {
3684 let span = self.sp(expr.range);
3685 if expr.ty == expected {
3686 match &expr.kind {
3687 ExprKind::Int(value) => return bare_int_literal(*value, expected, span),
3688 ExprKind::Float(value) if value.is_finite() => {
3689 return bare_float_literal(*value, span);
3690 }
3691 ExprKind::Cond { .. } if !expected.is_void() => {
3696 return self.cond_chain_at(expr, expected);
3697 }
3698 _ => {}
3699 }
3700 }
3701 self.expr(expr).at(prec::LOWEST, span)
3702 }
3703
3704 fn expr(&mut self, expr: &Expr) -> Value {
3705 let value = self.expr_value(expr);
3706 if matches!(expr.kind, ExprKind::Binary { .. }) {
3709 return value;
3710 }
3711 self.reduce_bits(value, expr)
3712 }
3713
3714 fn reduce_bits(&mut self, value: Value, expr: &Expr) -> Value {
3722 let Some(bits) = expr.bits else {
3723 return value;
3724 };
3725 let width = expr.ty.bits(&self.options.target);
3726 let overflows = match &expr.kind {
3727 ExprKind::Binary { op, .. } => {
3728 matches!(op, BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Shl)
3729 }
3730 ExprKind::Neg(_) | ExprKind::BitNot(_) => true,
3731 _ => false,
3732 };
3733 if !overflows || !expr.ty.is_integer() || bits == 0 || bits >= width {
3734 return value;
3735 }
3736 let span = self.sp(expr.range);
3737 if expr.ty.is_signed(&self.options.target) {
3738 let shift = Literal::u32_unsuffixed(width - bits);
3741 let tokens = value.at(prec::CALL, span);
3742 return Value::new(
3743 quote_spanned! {span=> #tokens.wrapping_shl(#shift).wrapping_shr(#shift) },
3744 prec::CALL,
3745 );
3746 }
3747 let mask = bare_int_literal(((1u128 << bits) - 1) as i128, expr.ty, span);
3748 let tokens = value.at(prec::BIT_AND, span);
3749 Value::new(quote_spanned! {span=> #tokens & #mask }, prec::BIT_AND)
3750 }
3751
3752 fn expr_value(&mut self, expr: &Expr) -> Value {
3753 let span = self.sp(expr.range);
3754 match &expr.kind {
3755 ExprKind::Int(value) => self.int_literal(*value, expr.ty, span),
3756 ExprKind::Float(value) => self.float_literal(*value, expr.ty, span),
3757 ExprKind::Zeroed => Value::new(self.zero_tokens(expr.ty, span), zero_prec(expr.ty)),
3758 ExprKind::Load(place) if place.ty.is_va_list() => self.va_list_load(place, span),
3761 ExprKind::Load(place) => {
3762 let lowered = self.place(place, false);
3763 let value = self.read(&lowered, span);
3764 if lowered.setup.is_empty() {
3765 value
3766 } else {
3767 let setup = &lowered.setup;
3768 let tokens = value.at(prec::LOWEST, span);
3769 Value::new(quote_spanned! {span=> { #setup #tokens } }, prec::BLOCK)
3770 }
3771 }
3772 ExprKind::AddrOf(place) => self.address_of(place, expr.ty, span),
3773 ExprKind::FuncAddr(id) => {
3774 let function = self.program.function(*id);
3775 let name = self.function_path(function, span);
3776 let signature = self.function_pointer_ty(function, span);
3777 Value::new(
3778 quote_spanned! {span=> ::core::option::Option::Some(#name as #signature) },
3779 prec::CALL,
3780 )
3781 }
3782 ExprKind::LabelAddr(id) => self.label_address(*id, expr.ty, span),
3787 ExprKind::Assign { .. } => self.assign_chain(expr),
3788 ExprKind::CompoundAssign {
3789 place,
3790 op,
3791 value,
3792 compute,
3793 } => {
3794 let lowered = self.place(place, true);
3795 if lowered.atomic.is_some() {
3796 let kind = PlaceRmw::Compound {
3797 op: *op,
3798 value,
3799 compute: *compute,
3800 };
3801 let tokens = self.atomic_place_rmw(&lowered, kind, RmwValue::New, span);
3802 return Value::new(tokens, prec::BLOCK);
3803 }
3804 let (hoist, rhs) = self.compound_rhs(value);
3805 let current = self.read(&lowered, span);
3806 let updated = self.compound_value(current, place.ty, *op, value, rhs, *compute);
3807 let updated = updated.at(prec::LOWEST, span);
3808 let store = self.write(&lowered, updated, span);
3809 let read = self.read(&lowered, span).at(prec::LOWEST, span);
3810 let setup = &lowered.setup;
3811 Value::new(
3812 quote_spanned! {span=> { #setup #hoist #store #read } },
3813 prec::BLOCK,
3814 )
3815 }
3816 ExprKind::IncDec {
3817 place,
3818 dec,
3819 postfix,
3820 } => {
3821 let lowered = self.place(place, true);
3822 if lowered.atomic.is_some() {
3823 let want = if *postfix {
3824 RmwValue::Old
3825 } else {
3826 RmwValue::New
3827 };
3828 let tokens =
3829 self.atomic_place_rmw(&lowered, PlaceRmw::Step { dec: *dec }, want, span);
3830 return Value::new(tokens, prec::BLOCK);
3831 }
3832 let current = self.read(&lowered, span);
3833 let next = self.step_value(current, place.ty, *dec, span);
3834 let store = self.write(&lowered, next, span);
3835 let read = self.read(&lowered, span).at(prec::LOWEST, span);
3836 let setup = &lowered.setup;
3837 if *postfix {
3838 let tmp = self.temporary();
3839 Value::new(
3840 quote_spanned! {span=>
3841 { #setup let #tmp = #read; #store #tmp }
3842 },
3843 prec::BLOCK,
3844 )
3845 } else {
3846 Value::new(
3847 quote_spanned! {span=> { #setup #store #read } },
3848 prec::BLOCK,
3849 )
3850 }
3851 }
3852 ExprKind::ComplexOf { re, im } => self.complex_literal(expr.ty, re, im, span),
3856 ExprKind::Neg(operand) if expr.ty.is_complex() => self.complex_neg(operand, span),
3857 ExprKind::BitNot(operand) if expr.ty.is_complex() => {
3858 self.complex_conj(operand, expr.ty, span)
3859 }
3860 ExprKind::Neg(operand) => {
3861 let value = self.expr(operand);
3862 if expr.ty.is_floating() {
3863 let ends_with_type = value.ends_with_type;
3864 let tokens = value.at(prec::UNARY, span);
3865 Value::new(quote_spanned! {span=> -#tokens }, prec::UNARY)
3866 .type_end(ends_with_type)
3867 } else {
3868 let tokens = value.at(prec::CALL, span);
3872 Value::new(quote_spanned! {span=> #tokens.wrapping_neg() }, prec::CALL)
3873 }
3874 }
3875 ExprKind::BitNot(operand) => {
3876 let value = self.expr(operand);
3877 let ends_with_type = value.ends_with_type;
3878 let tokens = value.at(prec::UNARY, span);
3879 Value::new(quote_spanned! {span=> !#tokens }, prec::UNARY).type_end(ends_with_type)
3880 }
3881 ExprKind::Binary { .. } => self.binary_chain(expr),
3882 ExprKind::PtrOffset { ptr, index, sub } => {
3883 let pointee = self.program.types.pointee(ptr.ty).unwrap_or(Ty::Void);
3884 let base = self.expr(ptr).at(prec::CALL, span);
3885 let offset = self.scaled_offset(pointee, index, *sub, span);
3886 Value::new(quote_spanned! {span=> #base.offset(#offset) }, prec::CALL)
3887 }
3888 ExprKind::PtrDiff { lhs, rhs }
3894 if label_state(lhs).is_some() && label_state(rhs).is_some() =>
3895 {
3896 self.label_difference(lhs, rhs, expr.ty, span)
3897 }
3898 ExprKind::PtrDiff { lhs, rhs } => {
3899 let pointee = self.program.types.pointee(lhs.ty).unwrap_or(Ty::Void);
3900 let scale = self.vm_scale(pointee, span);
3901 let left = self.expr(lhs).at(prec::CALL, span);
3902 let right = self.expr(rhs).at(prec::LOWEST, span);
3903 let target = self.ty(expr.ty, span);
3904 let difference = match scale {
3907 None => quote_spanned! {span=> #left.offset_from(#right) },
3908 Some(scale) => {
3909 quote_spanned! {span=> (#left.offset_from(#right) / (#scale)) }
3910 }
3911 };
3912 Value::new(quote_spanned! {span=> #difference as #target }, prec::CAST)
3913 .type_end(true)
3914 }
3915 ExprKind::Compare { .. } | ExprKind::Logical { .. } => {
3916 let condition = self.condition(expr).at(prec::LOWEST, span);
3918 let ty = self.ty(Ty::Int, span);
3919 let parens = parenthesize(condition, span);
3920 Value::new(quote_spanned! {span=> #parens as #ty }, prec::CAST).type_end(true)
3921 }
3922 ExprKind::Cast(inner) if expr.ty.is_void() => {
3929 let tokens = self.expr_stmt(inner);
3930 Value::new(quote_spanned! {span=> { #tokens } }, prec::BLOCK)
3931 }
3932 ExprKind::Cond { .. } if expr.ty.is_void() => {
3933 let tokens = self.expr_stmt(expr);
3934 Value::new(quote_spanned! {span=> { #tokens } }, prec::BLOCK)
3935 }
3936 ExprKind::Cast(inner) => {
3937 let from = inner.ty;
3938 let value = self.expr(inner);
3939 self.cast(value, from, expr.ty, span)
3940 }
3941 ExprKind::Cond { .. } => self.cond_chain(expr),
3949 ExprKind::Comma { lhs, rhs } => {
3950 let lhs = self.expr_stmt(lhs);
3951 let rhs = self.expr(rhs).at(prec::LOWEST, span);
3952 Value::new(quote_spanned! {span=> { #lhs #rhs } }, prec::BLOCK)
3953 }
3954 ExprKind::Call { callee, args } => self.call(callee, args, span),
3955 ExprKind::RecordLit { record, fields } => self.record_literal(*record, fields, span),
3956 ExprKind::UnionLit {
3957 record,
3958 index,
3959 value,
3960 } => self.union_literal(*record, *index, value, span),
3961 ExprKind::ArrayLit(items) => {
3962 let elem = self.program.types.elem(expr.ty).unwrap_or(Ty::Int);
3963 let mut tokens = TokenStream::new();
3964 for (index, item) in items.iter().enumerate() {
3965 if index > 0 {
3966 tokens.extend(quote_spanned! {span=> , });
3967 }
3968 tokens.extend(self.expr_at(item, elem));
3969 }
3970 Value::atom(bracketed(tokens, span))
3971 }
3972 ExprKind::ArrayRepeat { value, len } => {
3973 let elem = self.program.types.elem(expr.ty).unwrap_or(value.ty);
3974 let tokens = self.expr_at(value, elem);
3975 let len = usize_literal(*len, span);
3976 Value::atom(bracketed(quote_spanned! {span=> #tokens ; #len }, span))
3977 }
3978 ExprKind::CondDefault { value, else_expr } => {
3981 let ty = expr.ty;
3982 let first = self.expr_at(value, ty);
3983 let other = self.expr_at(else_expr, ty);
3984 let tmp = self.temporary();
3985 let target = self.ty(ty, span);
3986 let test = if ty.is_pointer() {
3987 quote_spanned! {span=> !#tmp.is_null() }
3988 } else {
3989 let zero = self.zero_tokens(ty, span);
3990 quote_spanned! {span=> #tmp != #zero }
3991 };
3992 Value::new(
3993 quote_spanned! {span=>
3994 { let #tmp: #target = #first; if #test { #tmp } else { #other } }
3995 },
3996 prec::BLOCK,
3997 )
3998 }
3999 ExprKind::StmtExpr { stmts, value } => {
4001 let body = self.stmts(stmts);
4002 let tail = match value {
4003 Some(value) => {
4004 let ty = expr.ty;
4005 self.expr_at(value, ty)
4006 }
4007 None => TokenStream::new(),
4008 };
4009 Value::new(quote_spanned! {span=> { #body #tail } }, prec::BLOCK)
4010 }
4011 ExprKind::Builtin { op, args } => self.builtin(*op, args, span),
4012 ExprKind::Atomic(atomic) => self.atomic(atomic, span),
4013 ExprKind::VaListPristine => Value::new(self.va_pristine(span), prec::CALL),
4014 ExprKind::VaArg { ap, record } => self.va_arg(ap, record.as_deref(), expr.ty, span),
4015 ExprKind::VaEnd => Value::atom(quote_spanned! {span=> () }),
4017 ExprKind::Unreachable => Value::new(
4022 quote_spanned! {span=> ::core::hint::unreachable_unchecked() },
4023 prec::CALL,
4024 ),
4025 }
4026 }
4027
4028 fn complex_new(&self, ty: Ty, re: TokenStream, im: TokenStream, span: Span) -> Value {
4038 self.uses_complex.set(true);
4039 let rt = self.rt_path(span);
4040 let component = primitive_ty(if ty == Ty::ComplexFloat { "f32" } else { "f64" }, span);
4041 Value::new(
4042 quote_spanned! {span=> #rt::Complex::<#component>::new(#re, #im) },
4043 prec::CALL,
4044 )
4045 }
4046
4047 fn complex_literal(&mut self, ty: Ty, re: &Expr, im: &Expr, span: Span) -> Value {
4050 let re = self.expr(re).at(prec::LOWEST, span);
4051 let im = self.expr(im).at(prec::LOWEST, span);
4052 self.complex_new(ty, re, im, span)
4053 }
4054
4055 fn complex_neg(&mut self, operand: &Expr, span: Span) -> Value {
4057 let tokens = self.expr(operand).at(prec::UNARY, span);
4058 Value::new(quote_spanned! {span=> -#tokens }, prec::UNARY)
4059 }
4060
4061 fn complex_conj(&mut self, operand: &Expr, ty: Ty, span: Span) -> Value {
4063 let tokens = self.expr(operand).at(prec::LOWEST, span);
4064 let conj = self.rt_complex(&format!("conj_{}", Self::complex_suffix(ty)), span);
4065 Value::new(quote_spanned! {span=> #conj(#tokens) }, prec::CALL)
4066 }
4067
4068 fn complex_binary(
4077 &mut self,
4078 op: BinOp,
4079 (lhs, lhs_ty): (Value, Ty),
4080 (rhs, rhs_ty): (Value, Ty),
4081 ty: Ty,
4082 span: Span,
4083 ) -> Value {
4084 self.uses_complex.set(true);
4085 let suffix = Self::complex_suffix(ty);
4086 let both = lhs_ty.is_complex() && rhs_ty.is_complex();
4087 if both && matches!(op, BinOp::Add | BinOp::Sub) {
4088 let (level, tokens) = match op {
4089 BinOp::Add => (prec::SUM, quote_spanned! {span=> + }),
4090 _ => (prec::SUM, quote_spanned! {span=> - }),
4091 };
4092 let mut out = lhs.at(level, span);
4093 let rhs = rhs.at(level + 1, span);
4094 out.extend(quote_spanned! {span=> #tokens #rhs });
4095 return Value::new(out, level);
4096 }
4097 let name = match (op, lhs_ty.is_complex(), rhs_ty.is_complex()) {
4100 (BinOp::Add, true, false) => "add_real",
4101 (BinOp::Add, false, true) => "real_add",
4102 (BinOp::Sub, true, false) => "sub_real",
4103 (BinOp::Sub, false, true) => "real_sub",
4104 (BinOp::Mul, true, true) => "mul",
4105 (BinOp::Mul, true, false) => "mul_real",
4106 (BinOp::Mul, false, true) => "real_mul",
4107 (BinOp::Div, true, true) => "div",
4108 (BinOp::Div, true, false) => "div_real",
4109 (BinOp::Div, false, true) => "real_div",
4110 _ => unreachable!("'{}' does not reach complex code generation", op.as_str()),
4113 };
4114 let func = self.rt_complex(&format!("{name}_{suffix}"), span);
4115 let lhs = lhs.at(prec::LOWEST, span);
4116 let rhs = rhs.at(prec::LOWEST, span);
4117 Value::new(quote_spanned! {span=> #func(#lhs, #rhs) }, prec::CALL)
4118 }
4119
4120 fn complex_cast(&mut self, value: Value, from: Ty, to: Ty, span: Span) -> Value {
4123 self.uses_complex.set(true);
4124 if from.is_complex() && to.is_complex() {
4125 let name = if to == Ty::ComplexDouble {
4126 "widen_f32"
4127 } else {
4128 "narrow_f64"
4129 };
4130 let func = self.rt_complex(name, span);
4131 let tokens = value.at(prec::LOWEST, span);
4132 return Value::new(quote_spanned! {span=> #func(#tokens) }, prec::CALL);
4133 }
4134 if to.is_complex() {
4135 let component = to.complex_component();
4137 let re = self
4138 .cast(value, from, component, span)
4139 .at(prec::LOWEST, span);
4140 let zero = bare_float_literal(0.0, span);
4141 return self.complex_new(to, re, zero, span);
4142 }
4143 if to.is_bool() {
4147 let func = self.rt_complex(&format!("nonzero_{}", Self::complex_suffix(from)), span);
4148 let tokens = value.at(prec::LOWEST, span);
4149 return Value::new(quote_spanned! {span=> #func(#tokens) }, prec::CALL);
4150 }
4151 let tokens = value.at(prec::CALL, span);
4152 let real = Value::new(quote_spanned! {span=> #tokens.re }, prec::CALL);
4153 self.cast(real, from.complex_component(), to, span)
4154 }
4155
4156 fn complex_equality(&mut self, op: CmpOp, lhs: &Expr, rhs: &Expr, span: Span) -> Value {
4169 self.uses_complex.set(true);
4170 let name = match op {
4171 CmpOp::Eq => "eq",
4172 CmpOp::Ne => "ne",
4173 other => unreachable!("{other:?} does not reach a complex comparison"),
4176 };
4177 let func = self.rt_complex(&format!("{name}_{}", Self::complex_suffix(lhs.ty)), span);
4178 let (lhs, rhs) = self.operands(lhs, rhs, BinOp::BitOr);
4179 let lhs = lhs.at(prec::LOWEST, span);
4180 let rhs = rhs.at(prec::LOWEST, span);
4181 Value::new(quote_spanned! {span=> #func(#lhs, #rhs) }, prec::CALL)
4182 }
4183
4184 fn complex_condition(&mut self, expr: &Expr, span: Span) -> Value {
4187 self.uses_complex.set(true);
4188 let func = self.rt_complex(&format!("nonzero_{}", Self::complex_suffix(expr.ty)), span);
4189 let tokens = self.expr(expr).at(prec::LOWEST, span);
4190 Value::new(quote_spanned! {span=> #func(#tokens) }, prec::CALL)
4191 }
4192
4193 fn record_literal(&mut self, record: ir::RecordId, fields: &[Expr], span: Span) -> Value {
4203 let def = self.program.types.record(record).clone();
4204 let tail = def
4208 .fields
4209 .iter()
4210 .position(|field| field.flexible)
4211 .and_then(|index| {
4212 Some((
4213 index,
4214 array_len(&self.program.types, fields.get(index)?.ty)?,
4215 ))
4216 })
4217 .filter(|(_, len)| *len > 0);
4218 let name = match tail {
4219 Some((_, len)) => self.flexible_ident(&def.rust_name, len, span),
4220 None => self.c_ident(&def.rust_name, span),
4221 };
4222 let mut packed: HashMap<String, Vec<u8>> = HashMap::new();
4223 let mut dynamic: Vec<usize> = Vec::new();
4224 for rust_field in &def.rust_fields {
4225 if let ir::RustField::Bits { name, bytes, .. } = rust_field {
4226 packed.insert(name.clone(), vec![0u8; *bytes as usize]);
4227 }
4228 }
4229 for (index, field) in def.fields.iter().enumerate() {
4230 let Some(bits) = &field.bits else { continue };
4231 match fields.get(index).and_then(constant_bits) {
4232 Some(value) => {
4233 if let Some(storage) = packed.get_mut(&bits.storage) {
4234 pack_bits(storage, bits, value);
4235 }
4236 }
4237 None => dynamic.push(index),
4238 }
4239 }
4240
4241 let mut items = TokenStream::new();
4242 for rust_field in &def.rust_fields {
4243 match rust_field {
4244 ir::RustField::Member(index) => {
4245 let field = &def.fields[*index];
4246 let fname = self.c_ident(&field.name, span);
4247 let value = &fields[*index];
4250 let want = match tail {
4251 Some((flexible, _)) if flexible == *index => value.ty,
4252 _ => field.ty,
4253 };
4254 let tokens = self.expr_at(value, want);
4255 items.extend(quote_spanned! {span=> #fname: #tokens, });
4256 }
4257 ir::RustField::Bits { name, .. } => {
4258 let fname = Ident::new(name, span);
4259 let value = byte_array(&packed[name], span);
4260 items.extend(quote_spanned! {span=> #fname: #value, });
4261 }
4262 ir::RustField::Pad { name, bytes } => {
4263 let fname = Ident::new(name, span);
4264 let len = usize_literal(*bytes, span);
4265 items.extend(quote_spanned! {span=> #fname: [0; #len], });
4266 }
4267 ir::RustField::Align { name, .. } => {
4268 let fname = Ident::new(name, span);
4269 items.extend(quote_spanned! {span=> #fname: [], });
4270 }
4271 }
4272 }
4273 let body = braced(items, span);
4274 let literal = quote_spanned! {span=> #name #body };
4275 if dynamic.is_empty() {
4276 return Value::new(literal, prec::ATOM);
4277 }
4278 let tmp = self.temporary();
4281 let ty = self.ty(Ty::Record(record), span);
4282 let mut stores = TokenStream::new();
4283 for index in dynamic {
4284 let field = &def.fields[index];
4285 let bits = field.bits.as_ref().expect("only bit-fields are deferred");
4286 let setter = self.c_ident(&bits.setter, span);
4287 let value = self.expr_at(&fields[index], field.ty);
4288 stores.extend(quote_spanned! {span=> #tmp.#setter(#value); });
4289 }
4290 Value::new(
4291 quote_spanned! {span=>
4292 { let mut #tmp: #ty = #literal; #stores #tmp }
4293 },
4294 prec::BLOCK,
4295 )
4296 }
4297
4298 fn union_literal(
4300 &mut self,
4301 record: ir::RecordId,
4302 index: usize,
4303 value: &Expr,
4304 span: Span,
4305 ) -> Value {
4306 let def = self.program.types.record(record).clone();
4307 let name = self.c_ident(&def.rust_name, span);
4308 let field = &def.fields[index];
4309 let Some(bits) = &field.bits else {
4310 let fname = self.c_ident(&field.name, span);
4311 let tokens = self.expr_at(value, field.ty);
4312 let body = braced(quote_spanned! {span=> #fname: #tokens }, span);
4313 return Value::new(quote_spanned! {span=> #name #body }, prec::ATOM);
4314 };
4315 let bytes = def
4316 .rust_fields
4317 .iter()
4318 .find_map(|rust_field| match rust_field {
4319 ir::RustField::Bits { name, bytes, .. } if *name == bits.storage => Some(*bytes),
4320 _ => None,
4321 })
4322 .unwrap_or(0);
4323 let mut packed = vec![0u8; bytes as usize];
4324 let constant = constant_bits(value);
4325 if let Some(constant) = constant {
4326 pack_bits(&mut packed, bits, constant);
4327 }
4328 let storage = Ident::new(&bits.storage, span);
4329 let array = byte_array(&packed, span);
4330 let body = braced(quote_spanned! {span=> #storage: #array }, span);
4331 let literal = quote_spanned! {span=> #name #body };
4332 if constant.is_some() {
4333 return Value::new(literal, prec::ATOM);
4334 }
4335 let tmp = self.temporary();
4336 let ty = self.ty(Ty::Record(record), span);
4337 let setter = self.c_ident(&bits.setter, span);
4338 let value = self.expr_at(value, field.ty);
4339 Value::new(
4340 quote_spanned! {span=>
4341 { let mut #tmp: #ty = #literal; #tmp.#setter(#value); #tmp }
4342 },
4343 prec::BLOCK,
4344 )
4345 }
4346
4347 fn builtin(&mut self, op: ir::BuiltinOp, args: &[Expr], span: Span) -> Value {
4356 use ir::BuiltinOp;
4357 let int = self.ty(Ty::Int, span);
4358 match op {
4359 BuiltinOp::ComplexProj => {
4360 let ty = args[0].ty;
4361 let func = self.rt_complex(&format!("proj_{}", Self::complex_suffix(ty)), span);
4362 let value = self.expr(&args[0]).at(prec::LOWEST, span);
4363 Value::new(quote_spanned! {span=> #func(#value) }, prec::CALL)
4364 }
4365 BuiltinOp::Discard => {
4366 let mut out = TokenStream::new();
4367 for arg in args {
4368 out.extend(self.expr_stmt(arg));
4369 }
4370 Value::new(quote_spanned! {span=> { #out } }, prec::BLOCK)
4371 }
4372 BuiltinOp::Alloca => {
4377 let arena = self.alloca_ident();
4378 let size = self.expr(&args[0]).at(prec::CAST, span);
4379 let block = self.temporary();
4380 let pointer = self.temporary();
4381 let void = self.pointee_ty(Ty::Void, span);
4382 let usize_ty = primitive_ty("usize", span);
4383 let elements = self.vec_of(
4384 quote_spanned! {span=> 0u128 },
4385 quote_spanned! {span=> (#size as #usize_ty).div_ceil(16) },
4386 span,
4387 );
4388 Value::new(
4389 quote_spanned! {span=>
4390 {
4391 let mut #block = #elements;
4392 let #pointer = #block.as_mut_ptr().cast::<#void>();
4393 #arena.push(#block);
4394 #pointer
4395 }
4396 },
4397 prec::BLOCK,
4398 )
4399 }
4400 BuiltinOp::Bswap => {
4401 let operand = args[0].ty;
4402 let value = self.unsigned_operand(&args[0], span);
4403 let target = self.ty(operand, span);
4404 Value::new(
4405 quote_spanned! {span=> #value.swap_bytes() as #target },
4406 prec::CAST,
4407 )
4408 .type_end(true)
4409 }
4410 BuiltinOp::Popcount => {
4411 let value = self.unsigned_operand(&args[0], span);
4412 Value::new(
4413 quote_spanned! {span=> #value.count_ones() as #int },
4414 prec::CAST,
4415 )
4416 .type_end(true)
4417 }
4418 BuiltinOp::Parity => {
4419 let value = self.unsigned_operand(&args[0], span);
4420 Value::new(
4421 quote_spanned! {span=> (#value.count_ones() & 1) as #int },
4422 prec::CAST,
4423 )
4424 .type_end(true)
4425 }
4426 BuiltinOp::Clz => {
4427 let value = self.unsigned_operand(&args[0], span);
4428 Value::new(
4429 quote_spanned! {span=> #value.leading_zeros() as #int },
4430 prec::CAST,
4431 )
4432 .type_end(true)
4433 }
4434 BuiltinOp::Ctz => {
4435 let value = self.unsigned_operand(&args[0], span);
4436 Value::new(
4437 quote_spanned! {span=> #value.trailing_zeros() as #int },
4438 prec::CAST,
4439 )
4440 .type_end(true)
4441 }
4442 BuiltinOp::Ffs => {
4443 let value = self.unsigned_operand(&args[0], span);
4444 let tmp = self.temporary();
4445 Value::new(
4446 quote_spanned! {span=>
4447 { let #tmp = #value;
4448 if #tmp == 0 { 0 } else { #tmp.trailing_zeros() as #int + 1 } }
4449 },
4450 prec::BLOCK,
4451 )
4452 }
4453 BuiltinOp::Clrsb => {
4457 let width = args[0].ty.bits(&self.options.target);
4458 let signed = signed_rust_ty(width, span);
4459 let value = self.expr(&args[0]).at(prec::CAST, span);
4460 let tmp = self.temporary();
4461 let bits = usize_literal(u64::from(width), span);
4462 Value::new(
4463 quote_spanned! {span=>
4464 { let #tmp = #value as #signed;
4465 ((#tmp ^ (#tmp << 1)).leading_zeros() as #int)
4466 .min(#bits as #int - 1) }
4467 },
4468 prec::BLOCK,
4469 )
4470 }
4471 BuiltinOp::Overflow(bin) | BuiltinOp::OverflowP(bin) => {
4472 let store = matches!(op, BuiltinOp::Overflow(_));
4473 let result_ty = if store {
4477 self.program.types.pointee(args[2].ty).unwrap_or(Ty::Int)
4478 } else {
4479 args[2].ty
4480 };
4481 self.overflow_builtin(bin, args, store, result_ty, span)
4482 }
4483 BuiltinOp::Fabs => {
4484 let bits = self.float_bits_of(&args[0], span);
4485 let (float_ty, mask) = float_bit_ty(args[0].ty, span);
4486 Value::new(
4487 quote_spanned! {span=> <#float_ty>::from_bits(#bits & #mask) },
4488 prec::CALL,
4489 )
4490 }
4491 BuiltinOp::Copysign => {
4492 let magnitude = self.float_bits_of(&args[0], span);
4493 let sign = self.float_bits_of(&args[1], span);
4494 let (float_ty, mask) = float_bit_ty(args[0].ty, span);
4495 Value::new(
4496 quote_spanned! {span=>
4497 <#float_ty>::from_bits((#magnitude & #mask) | (#sign & !#mask))
4498 },
4499 prec::CALL,
4500 )
4501 }
4502 BuiltinOp::FloatOrder(order) => self.float_order(order, args, span),
4503 BuiltinOp::FloatClass(class) => self.float_class(class, &args[0], span),
4504 BuiltinOp::Fpclassify => {
4505 let value = self.expr(&args[5]).at(prec::CALL, span);
4506 let arms = ["Nan", "Infinite", "Normal", "Subnormal", "Zero"]
4507 .iter()
4508 .zip(args)
4509 .map(|(name, answer)| {
4510 let variant = Ident::new(name, span);
4511 let answer = self.expr_at(answer, Ty::Int);
4512 quote_spanned! {span=>
4513 ::core::num::FpCategory::#variant => #answer,
4514 }
4515 })
4516 .collect::<TokenStream>();
4517 Value::new(
4518 quote_spanned! {span=> match #value.classify() { #arms } },
4519 prec::BLOCK,
4520 )
4521 }
4522 }
4523 }
4524
4525 fn float_bits_of(&mut self, arg: &Expr, span: Span) -> TokenStream {
4527 let value = self.expr(arg).at(prec::CALL, span);
4528 parenthesize(quote_spanned! {span=> #value.to_bits() }, span)
4529 }
4530
4531 fn float_order(&mut self, order: ir::FloatOrder, args: &[Expr], span: Span) -> Value {
4537 use ir::FloatOrder;
4538 let int = self.ty(Ty::Int, span);
4539 let lhs = self.expr(&args[0]).at(prec::LOWEST, span);
4540 let rhs = self.expr(&args[1]).at(prec::LOWEST, span);
4541 let (a, b) = (self.temporary(), self.temporary());
4542 let test = match order {
4543 FloatOrder::Greater => quote_spanned! {span=> #a > #b },
4544 FloatOrder::GreaterEqual => quote_spanned! {span=> #a >= #b },
4545 FloatOrder::Less => quote_spanned! {span=> #a < #b },
4546 FloatOrder::LessEqual => quote_spanned! {span=> #a <= #b },
4547 FloatOrder::LessGreater => quote_spanned! {span=> #a < #b || #a > #b },
4548 FloatOrder::Unordered => quote_spanned! {span=> #a.is_nan() || #b.is_nan() },
4549 };
4550 Value::new(
4551 quote_spanned! {span=>
4552 { let #a = #lhs; let #b = #rhs; (#test) as #int }
4553 },
4554 prec::BLOCK,
4555 )
4556 }
4557
4558 fn float_class(&mut self, class: ir::FloatClass, arg: &Expr, span: Span) -> Value {
4565 use ir::FloatClass;
4566 let int = self.ty(Ty::Int, span);
4567 let method = |name: &str| Ident::new(name, span);
4568 let test = match class {
4569 FloatClass::IsNan => Some(method("is_nan")),
4570 FloatClass::IsInf => Some(method("is_infinite")),
4571 FloatClass::IsFinite => Some(method("is_finite")),
4572 FloatClass::IsNormal => Some(method("is_normal")),
4573 FloatClass::SignBit => Some(method("is_sign_negative")),
4574 FloatClass::IsInfSign | FloatClass::IsSignaling => None,
4575 };
4576 if let Some(test) = test {
4577 let value = self.expr(arg).at(prec::CALL, span);
4578 return Value::new(quote_spanned! {span=> #value.#test() as #int }, prec::CAST)
4579 .type_end(true);
4580 }
4581 let tmp = self.temporary();
4582 let value = self.expr(arg).at(prec::LOWEST, span);
4583 if class == FloatClass::IsInfSign {
4584 return Value::new(
4585 quote_spanned! {span=>
4586 { let #tmp = #value;
4587 if #tmp.is_infinite() {
4588 if #tmp.is_sign_negative() { -1 as #int } else { 1 as #int }
4589 } else { 0 as #int } }
4590 },
4591 prec::BLOCK,
4592 );
4593 }
4594 let quiet = quiet_bit_literal(arg.ty, span);
4597 Value::new(
4598 quote_spanned! {span=>
4599 { let #tmp = #value;
4600 (#tmp.is_nan() && (#tmp.to_bits() & #quiet) == 0) as #int }
4601 },
4602 prec::BLOCK,
4603 )
4604 }
4605
4606 fn atomic_path(&self, class: AtomicClass, span: Span) -> TokenStream {
4610 if class == AtomicClass::Ptr {
4611 let void = self.pointee_ty(Ty::Void, span);
4612 return quote_spanned! {span=>
4613 ::core::sync::atomic::AtomicPtr::<#void>
4614 };
4615 }
4616 let name = Ident::new(class.rust_name(), span);
4617 quote_spanned! {span=> ::core::sync::atomic::#name }
4618 }
4619
4620 fn atomic_repr_ty(&self, class: AtomicClass, span: Span) -> TokenStream {
4626 if class == AtomicClass::Ptr {
4627 let void = self.pointee_ty(Ty::Void, span);
4628 return quote_spanned! {span=> *mut #void };
4629 }
4630 primitive_ty(class.repr_name(), span)
4631 }
4632
4633 fn atomic_ref(&self, class: AtomicClass, ptr: TokenStream, span: Span) -> TokenStream {
4642 let path = self.atomic_path(class, span);
4643 let repr = self.atomic_repr_ty(class, span);
4644 quote_spanned! {span=> #path::from_ptr(#ptr as *mut #repr) }
4645 }
4646
4647 fn ordering(&self, order: ir::MemOrder, span: Span) -> TokenStream {
4649 let name = Ident::new(order.rust_name(), span);
4650 quote_spanned! {span=> ::core::sync::atomic::Ordering::#name }
4651 }
4652
4653 fn repr_to_value(&self, class: AtomicClass, ty: Ty, value: TokenStream, span: Span) -> Value {
4655 match class {
4656 AtomicClass::Bool => Value::atom(value),
4657 AtomicClass::Float { bytes } => {
4658 let float = primitive_ty(if bytes == 4 { "f32" } else { "f64" }, span);
4659 Value::new(
4660 quote_spanned! {span=> <#float>::from_bits(#value) },
4661 prec::CALL,
4662 )
4663 }
4664 AtomicClass::Int { .. } | AtomicClass::Ptr => {
4665 let target = self.ty(ty, span);
4666 Value::new(quote_spanned! {span=> #value as #target }, prec::CAST).type_end(true)
4667 }
4668 }
4669 }
4670
4671 fn value_to_repr(&self, class: AtomicClass, value: Value, span: Span) -> TokenStream {
4673 match class {
4674 AtomicClass::Bool => value.at(prec::LOWEST, span),
4675 AtomicClass::Float { bytes } => {
4676 let float = primitive_ty(if bytes == 4 { "f32" } else { "f64" }, span);
4677 let value = value.at(prec::LOWEST, span);
4678 quote_spanned! {span=> <#float>::to_bits(#value) }
4679 }
4680 AtomicClass::Int { .. } | AtomicClass::Ptr => {
4681 let repr = self.atomic_repr_ty(class, span);
4682 let value = value.at(prec::CAST, span);
4683 quote_spanned! {span=> #value as #repr }
4684 }
4685 }
4686 }
4687
4688 fn either_way(&mut self, result: TokenStream, span: Span) -> TokenStream {
4691 let value = self.temporary();
4692 quote_spanned! {span=>
4693 match #result {
4694 ::core::result::Result::Ok(#value) | ::core::result::Result::Err(#value) => #value,
4695 }
4696 }
4697 }
4698
4699 fn atomic_cas_loop(
4713 &mut self,
4714 object: &TokenStream,
4715 current: &Ident,
4716 updated: TokenStream,
4717 order: ir::MemOrder,
4718 span: Span,
4719 ) -> TokenStream {
4720 let success = self.ordering(order, span);
4721 let failure = self.ordering(order.failure_order(), span);
4722 let slot = self.temporary();
4723 let fresh = self.temporary();
4724 let seen = self.temporary();
4725 quote_spanned! {span=>
4726 {
4727 let #slot = #object;
4728 let mut #current = #slot.load(#failure);
4729 loop {
4730 let #fresh = #updated;
4731 match #slot.compare_exchange_weak(#current, #fresh, #success, #failure) {
4732 ::core::result::Result::Ok(_) => break #current,
4733 ::core::result::Result::Err(#seen) => #current = #seen,
4734 }
4735 }
4736 }
4737 }
4738 }
4739
4740 fn atomic(&mut self, atomic: &ir::AtomicExpr, span: Span) -> Value {
4742 let class = atomic.class;
4743 let success = self.ordering(atomic.success, span);
4744 if let ir::AtomicOp::Fence { signal } = atomic.op {
4745 if atomic.success == ir::MemOrder::Relaxed {
4748 return Value::atom(quote_spanned! {span=> () });
4749 }
4750 let name = Ident::new(if signal { "compiler_fence" } else { "fence" }, span);
4751 return Value::new(
4752 quote_spanned! {span=> ::core::sync::atomic::#name(#success) },
4753 prec::CALL,
4754 );
4755 }
4756 let ptr = match &atomic.ptr {
4757 Some(ptr) => self.expr(ptr).at(prec::CAST, span),
4758 None => return Value::atom(quote_spanned! {span=> () }),
4759 };
4760 let object = self.atomic_ref(class, ptr, span);
4761 let ty = atomic.value_ty;
4762 match atomic.op {
4763 ir::AtomicOp::Fence { .. } => unreachable!("handled above"),
4764 ir::AtomicOp::Load => self.repr_to_value(
4765 class,
4766 ty,
4767 quote_spanned! {span=> #object.load(#success) },
4768 span,
4769 ),
4770 ir::AtomicOp::Store => {
4771 let value = self.atomic_operand(atomic, span);
4772 Value::new(
4773 quote_spanned! {span=> #object.store(#value, #success) },
4774 prec::CALL,
4775 )
4776 }
4777 ir::AtomicOp::Exchange => {
4778 let value = self.atomic_operand(atomic, span);
4779 self.repr_to_value(
4780 class,
4781 ty,
4782 quote_spanned! {span=> #object.swap(#value, #success) },
4783 span,
4784 )
4785 }
4786 ir::AtomicOp::Clear => Value::new(
4787 quote_spanned! {span=> #object.store(0, #success) },
4788 prec::CALL,
4789 ),
4790 ir::AtomicOp::TestAndSet => Value::new(
4793 quote_spanned! {span=> #object.swap(1, #success) != 0 },
4794 prec::CMP,
4795 ),
4796 ir::AtomicOp::CompareExchange { weak } => {
4797 self.compare_exchange(atomic, object, weak, span)
4798 }
4799 ir::AtomicOp::SyncCompareSwap { value_is_old } => {
4800 self.sync_compare_swap(atomic, object, value_is_old, span)
4801 }
4802 ir::AtomicOp::Rmw { op, returns_new } => {
4803 self.atomic_rmw(atomic, object, op, returns_new, span)
4804 }
4805 }
4806 }
4807
4808 fn atomic_operand(&mut self, atomic: &ir::AtomicExpr, span: Span) -> TokenStream {
4810 let Some(value) = &atomic.value else {
4811 return quote_spanned! {span=> () };
4812 };
4813 let value = self.expr(value);
4814 self.value_to_repr(atomic.class, value, span)
4815 }
4816
4817 fn compare_exchange(
4820 &mut self,
4821 atomic: &ir::AtomicExpr,
4822 object: TokenStream,
4823 weak: bool,
4824 span: Span,
4825 ) -> Value {
4826 let class = atomic.class;
4827 let ty = atomic.value_ty;
4828 let expected = match &atomic.expected {
4829 Some(expected) => self.expr(expected).at(prec::CALL, span),
4830 None => return Value::atom(quote_spanned! {span=> false }),
4831 };
4832 let desired = self.atomic_operand(atomic, span);
4833 let slot = self.temporary();
4834 let seen = self.temporary();
4835 let current = self.value_to_repr(class, Value::atom(quote_spanned! {span=> *#slot }), span);
4836 let method = Ident::new(
4837 if weak {
4838 "compare_exchange_weak"
4839 } else {
4840 "compare_exchange"
4841 },
4842 span,
4843 );
4844 let success = self.ordering(atomic.success, span);
4845 let failure = self.ordering(atomic.failure, span);
4846 let observed = self
4847 .repr_to_value(class, ty, quote_spanned! {span=> #seen }, span)
4848 .at(prec::LOWEST, span);
4849 Value::new(
4850 quote_spanned! {span=>
4851 {
4852 let #slot = #expected;
4853 match #object.#method(#current, #desired, #success, #failure) {
4854 ::core::result::Result::Ok(_) => true,
4855 ::core::result::Result::Err(#seen) => {
4856 *#slot = #observed;
4857 false
4858 }
4859 }
4860 }
4861 },
4862 prec::BLOCK,
4863 )
4864 }
4865
4866 fn sync_compare_swap(
4869 &mut self,
4870 atomic: &ir::AtomicExpr,
4871 object: TokenStream,
4872 value_is_old: bool,
4873 span: Span,
4874 ) -> Value {
4875 let class = atomic.class;
4876 let ty = atomic.value_ty;
4877 let expected = match &atomic.expected {
4878 Some(expected) => {
4879 let value = self.expr(expected);
4880 self.value_to_repr(class, value, span)
4881 }
4882 None => return Value::atom(quote_spanned! {span=> false }),
4883 };
4884 let desired = self.atomic_operand(atomic, span);
4885 let seq = self.ordering(ir::MemOrder::SeqCst, span);
4886 let call = quote_spanned! {span=>
4887 #object.compare_exchange(#expected, #desired, #seq, #seq)
4888 };
4889 if !value_is_old {
4890 return Value::new(quote_spanned! {span=> #call.is_ok() }, prec::CALL);
4891 }
4892 let old = self.either_way(call, span);
4893 self.repr_to_value(class, ty, parenthesize(old, span), span)
4894 }
4895
4896 fn atomic_rmw(
4899 &mut self,
4900 atomic: &ir::AtomicExpr,
4901 object: TokenStream,
4902 op: ir::AtomicRmw,
4903 returns_new: bool,
4904 span: Span,
4905 ) -> Value {
4906 let class = atomic.class;
4907 let ty = atomic.value_ty;
4908 let success = self.ordering(atomic.success, span);
4909 let operand = self.temporary();
4910 let old = self.temporary();
4911 if class == AtomicClass::Ptr {
4914 let delta = match &atomic.value {
4915 Some(value) => self.expr(value).at(prec::CAST, span),
4916 None => quote_spanned! {span=> 0 },
4917 };
4918 let isize_ty = primitive_ty("isize", span);
4919 let signed = if op == ir::AtomicRmw::Sub {
4920 quote_spanned! {span=> -(#delta as #isize_ty) }
4921 } else {
4922 quote_spanned! {span=> #delta as #isize_ty }
4923 };
4924 let step = self.temporary();
4925 let updated = self.atomic_cas_loop(
4926 &object,
4927 &step,
4928 quote_spanned! {span=> #step.wrapping_byte_offset(#operand) },
4929 atomic.success,
4930 span,
4931 );
4932 let tail = if returns_new {
4933 quote_spanned! {span=> #old.wrapping_byte_offset(#operand) }
4934 } else {
4935 quote_spanned! {span=> #old }
4936 };
4937 let tail = self
4938 .repr_to_value(class, ty, tail, span)
4939 .at(prec::LOWEST, span);
4940 return Value::new(
4941 quote_spanned! {span=>
4942 {
4943 let #operand: #isize_ty = #signed;
4944 let #old = #updated;
4945 #tail
4946 }
4947 },
4948 prec::BLOCK,
4949 );
4950 }
4951 let value = self.atomic_operand(atomic, span);
4952 let repr = self.atomic_repr_ty(class, span);
4953 let update = match op.rust_method() {
4956 Some(method) if op != ir::AtomicRmw::Nand || class == AtomicClass::Bool => {
4957 let method = Ident::new(method, span);
4958 quote_spanned! {span=> #object.#method(#operand, #success) }
4959 }
4960 _ if class == AtomicClass::Bool => {
4961 let method = Ident::new("fetch_nand", span);
4962 quote_spanned! {span=> #object.#method(#operand, #success) }
4963 }
4964 _ => {
4965 let current = self.temporary();
4966 self.atomic_cas_loop(
4967 &object,
4968 ¤t,
4969 quote_spanned! {span=> !(#current & #operand) },
4970 atomic.success,
4971 span,
4972 )
4973 }
4974 };
4975 let combined = self.atomic_combine(op, &old, &operand, span);
4976 let tail = if returns_new {
4977 combined
4978 } else {
4979 quote_spanned! {span=> #old }
4980 };
4981 let tail = self
4982 .repr_to_value(class, ty, tail, span)
4983 .at(prec::LOWEST, span);
4984 Value::new(
4985 quote_spanned! {span=>
4986 {
4987 let #operand: #repr = #value;
4988 let #old = #update;
4989 #tail
4990 }
4991 },
4992 prec::BLOCK,
4993 )
4994 }
4995
4996 fn atomic_place_rmw(
5010 &mut self,
5011 lowered: &LoweredPlace,
5012 kind: PlaceRmw<'_>,
5013 want: RmwValue,
5014 span: Span,
5015 ) -> TokenStream {
5016 let (class, ty) = lowered.atomic.expect("an atomic place");
5017 let object = self.atomic_object_of(lowered, span);
5018 let setup = &lowered.setup;
5019 let operand = self.temporary();
5020 let old = self.temporary();
5021 let success = self.ordering(ir::MemOrder::SeqCst, span);
5022 let method = match (class, &kind) {
5026 (
5027 AtomicClass::Int { .. },
5028 PlaceRmw::Compound {
5029 op,
5030 compute,
5031 value: _,
5032 },
5033 ) if *compute == ty => rmw_of_binop(*op),
5034 (AtomicClass::Int { .. }, PlaceRmw::Step { dec }) => Some(if *dec {
5035 ir::AtomicRmw::Sub
5036 } else {
5037 ir::AtomicRmw::Add
5038 }),
5039 _ => None,
5040 };
5041 if let Some(op) = method.filter(|op| op.rust_method().is_some()) {
5042 let repr = self.atomic_repr_ty(class, span);
5043 let value = match &kind {
5044 PlaceRmw::Compound { value, compute, .. } => {
5045 let tokens = self.expr_at(value, *compute);
5046 self.value_to_repr(class, Value::new(tokens, prec::LOWEST), span)
5047 }
5048 PlaceRmw::Step { .. } => quote_spanned! {span=> 1 },
5049 };
5050 let name = Ident::new(op.rust_method().expect("filtered"), span);
5051 let tail = self.atomic_rmw_tail((class, ty), op, &old, &operand, want, span);
5052 return quote_spanned! {span=>
5053 { #setup
5054 let #operand: #repr = #value;
5055 let #old = #object.#name(#operand, #success);
5056 #tail }
5057 };
5058 }
5059 let hoisted = match &kind {
5062 PlaceRmw::Compound { value, compute, .. } => {
5063 let tokens = self.expr_at(value, *compute);
5064 let rhs_ty = self.ty(*compute, span);
5065 Some(quote_spanned! {span=> let #operand: #rhs_ty = #tokens; })
5066 }
5067 PlaceRmw::Step { .. } => None,
5068 };
5069 let param = self.temporary();
5070 let current = self.repr_to_value(class, ty, quote_spanned! {span=> #param }, span);
5071 let updated = self.apply_place_rmw(&kind, current, ty, &operand, span);
5072 let updated = self.value_to_repr(class, updated, span);
5073 let loop_result =
5074 self.atomic_cas_loop(&object, ¶m, updated, ir::MemOrder::SeqCst, span);
5075 let tail = match want {
5076 RmwValue::None => TokenStream::new(),
5077 RmwValue::Old => self
5078 .repr_to_value(class, ty, quote_spanned! {span=> #old }, span)
5079 .at(prec::LOWEST, span),
5080 RmwValue::New => {
5081 let previous = self.repr_to_value(class, ty, quote_spanned! {span=> #old }, span);
5082 let value = self.apply_place_rmw(&kind, previous, ty, &operand, span);
5083 value.at(prec::LOWEST, span)
5084 }
5085 };
5086 quote_spanned! {span=>
5087 { #setup #hoisted
5088 let #old = #loop_result;
5089 #tail }
5090 }
5091 }
5092
5093 fn apply_place_rmw(
5095 &mut self,
5096 kind: &PlaceRmw<'_>,
5097 current: Value,
5098 ty: Ty,
5099 operand: &Ident,
5100 span: Span,
5101 ) -> Value {
5102 match kind {
5103 PlaceRmw::Compound { op, value, compute } => {
5104 let rhs = Value::atom(quote_spanned! {span=> #operand });
5105 self.compound_value(current, ty, *op, value, Some(rhs), *compute)
5106 }
5107 PlaceRmw::Step { dec } => {
5108 Value::new(self.step_value(current, ty, *dec, span), prec::LOWEST)
5109 }
5110 }
5111 }
5112
5113 fn atomic_rmw_tail(
5119 &mut self,
5120 atomic: (AtomicClass, Ty),
5121 op: ir::AtomicRmw,
5122 old: &Ident,
5123 operand: &Ident,
5124 want: RmwValue,
5125 span: Span,
5126 ) -> TokenStream {
5127 let (class, ty) = atomic;
5128 let value = match want {
5129 RmwValue::None => return TokenStream::new(),
5130 RmwValue::Old => quote_spanned! {span=> #old },
5131 RmwValue::New => self.atomic_combine(op, old, operand, span),
5132 };
5133 self.repr_to_value(class, ty, value, span)
5134 .at(prec::LOWEST, span)
5135 }
5136
5137 fn atomic_combine(
5140 &self,
5141 op: ir::AtomicRmw,
5142 old: &Ident,
5143 operand: &Ident,
5144 span: Span,
5145 ) -> TokenStream {
5146 match op {
5147 ir::AtomicRmw::Add => quote_spanned! {span=> #old.wrapping_add(#operand) },
5150 ir::AtomicRmw::Sub => quote_spanned! {span=> #old.wrapping_sub(#operand) },
5151 ir::AtomicRmw::And => quote_spanned! {span=> (#old & #operand) },
5152 ir::AtomicRmw::Or => quote_spanned! {span=> (#old | #operand) },
5153 ir::AtomicRmw::Xor => quote_spanned! {span=> (#old ^ #operand) },
5154 ir::AtomicRmw::Nand => quote_spanned! {span=> !(#old & #operand) },
5155 }
5156 }
5157
5158 fn unsigned_operand(&mut self, arg: &Expr, span: Span) -> TokenStream {
5161 let width = arg.ty.bits(&self.options.target);
5162 let target = unsigned_rust_ty(width, span);
5163 let value = self.expr(arg).at(prec::CAST, span);
5164 parenthesize(quote_spanned! {span=> #value as #target }, span)
5165 }
5166
5167 fn overflow_builtin(
5178 &mut self,
5179 op: BinOp,
5180 args: &[Expr],
5181 store: bool,
5182 result_ty: Ty,
5183 span: Span,
5184 ) -> Value {
5185 let method = match op {
5186 BinOp::Add => "wrapping_add",
5187 BinOp::Sub => "wrapping_sub",
5188 _ => "wrapping_mul",
5189 };
5190 let method = Ident::new(method, span);
5191 let checked = match op {
5192 BinOp::Add => "checked_add",
5193 BinOp::Sub => "checked_sub",
5194 _ => "checked_mul",
5195 };
5196 let checked = Ident::new(checked, span);
5197 let lhs = self.expr(&args[0]).at(prec::CAST, span);
5198 let rhs = self.expr(&args[1]).at(prec::CAST, span);
5199 let target = self.ty(result_ty, span);
5200 let a = self.temporary();
5201 let b = self.temporary();
5202 let wide = self.temporary();
5203 let narrow = self.temporary();
5204 let i128 = primitive_ty("i128", span);
5206 let compute = quote_spanned! {span=>
5207 let #a: #i128 = #lhs as #i128;
5208 let #b: #i128 = #rhs as #i128;
5209 let #wide: #i128 = #a.#method(#b);
5210 let #narrow: #target = #wide as #target;
5211 };
5212 let fits = match result_ty {
5216 Ty::Int128 => quote_spanned! {span=> false },
5219 Ty::UInt128 => quote_spanned! {span=> #wide < 0 },
5220 _ => quote_spanned! {span=> (#narrow as #i128) != #wide },
5221 };
5222 let flag = quote_spanned! {span=>
5223 #a.#checked(#b).is_none() || #fits
5224 };
5225 if !store {
5226 let third = self.expr_stmt(&args[2]);
5228 return Value::new(
5229 quote_spanned! {span=> { #third #compute #flag } },
5230 prec::BLOCK,
5231 );
5232 }
5233 let place = self.expr(&args[2]).at(prec::CALL, span);
5234 let out = self.temporary();
5235 Value::new(
5236 quote_spanned! {span=>
5237 { #compute let #out = #place; *#out = #narrow; #flag }
5238 },
5239 prec::BLOCK,
5240 )
5241 }
5242
5243 fn va_arg(
5261 &mut self,
5262 ap: &Place,
5263 record: Option<&[ir::Eightbyte]>,
5264 ty: Ty,
5265 span: Span,
5266 ) -> Value {
5267 let access = self.place(ap, true).access;
5268 if let Some(classes) = record {
5269 let target = self.ty(ty, span);
5270 let u64_ty = primitive_ty("u64", span);
5271 let f64_ty = primitive_ty("f64", span);
5272 let words = classes.iter().map(|class| match class {
5273 ir::Eightbyte::Int => quote_spanned! {span=> #access.next_arg::<#u64_ty>() },
5274 ir::Eightbyte::Sse => {
5275 quote_spanned! {span=> #access.next_arg::<#f64_ty>().to_bits() }
5276 }
5277 ir::Eightbyte::None => quote_spanned! {span=> 0 },
5280 });
5281 let count = Literal::usize_unsuffixed(classes.len());
5282 let value = self.temporary();
5283 return Value::new(
5284 quote_spanned! {span=>
5285 {
5286 let #value: [#u64_ty; #count] = [#(#words),*];
5287 ::core::ptr::read_unaligned(#value.as_ptr().cast::<#target>())
5288 }
5289 },
5290 prec::BLOCK,
5291 );
5292 }
5293 if self.program.types.is_func_pointer(ty) {
5294 let target = self.ty(ty, span);
5295 let void = self.pointee_ty(Ty::Void, span);
5296 return Value::new(
5297 quote_spanned! {span=>
5298 ::core::mem::transmute::<*mut #void, #target>(
5299 #access.next_arg::<*mut #void>()
5300 )
5301 },
5302 prec::CALL,
5303 );
5304 }
5305 let target = self.ty(ty, span);
5306 Value::new(
5307 quote_spanned! {span=> #access.next_arg::<#target>() },
5308 prec::CALL,
5309 )
5310 }
5311
5312 fn vm_scale(&self, pointee: Ty, span: Span) -> Option<TokenStream> {
5322 if !self.program.types.is_vm(pointee) {
5323 return None;
5324 }
5325 let isize_ty = primitive_ty("isize", span);
5326 let mut product: Option<TokenStream> = None;
5327 for dim in self.program.types.vm_dims(pointee).iter().rev() {
5328 let factor = match dim {
5329 ir::VmDim::Fixed(len) => {
5330 let literal = Literal::isize_unsuffixed(*len as isize);
5331 quote_spanned! {span=> #literal }
5332 }
5333 ir::VmDim::Len(id) => {
5334 let name = self.object_ident(*id, span);
5335 quote_spanned! {span=> #name as #isize_ty }
5336 }
5337 ir::VmDim::Unknown => quote_spanned! {span=> 1 },
5340 };
5341 product = Some(match product {
5342 None => factor,
5343 Some(left) => quote_spanned! {span=> (#left).wrapping_mul(#factor) },
5344 });
5345 }
5346 product
5347 }
5348
5349 fn scaled_offset(&mut self, pointee: Ty, index: &Expr, sub: bool, span: Span) -> TokenStream {
5352 let Some(scale) = self.vm_scale(pointee, span) else {
5353 return self.offset_argument(index, sub, span);
5354 };
5355 let isize_ty = primitive_ty("isize", span);
5356 let offset = match &index.kind {
5359 ExprKind::Int(value) => {
5360 let value = if sub { -*value } else { *value };
5361 let literal = int_literal_token(value, span);
5362 quote_spanned! {span=> (#literal as #isize_ty) }
5363 }
5364 _ => {
5365 let inner = self.offset_argument(index, sub, span);
5366 quote_spanned! {span=> (#inner) }
5367 }
5368 };
5369 quote_spanned! {span=> #offset.wrapping_mul(#scale) }
5370 }
5371
5372 fn offset_argument(&mut self, index: &Expr, sub: bool, span: Span) -> TokenStream {
5373 if let ExprKind::Int(value) = &index.kind {
5374 let value = if sub { value.wrapping_neg() } else { *value };
5375 let literal = int_literal_token(value, span);
5376 if value.unsigned_abs() > UNSUFFIXED_LIMIT as u128 {
5382 let isize_ty = primitive_ty("isize", span);
5383 return quote_spanned! {span=> (#literal as #isize_ty) };
5384 }
5385 return literal;
5386 }
5387 let tokens = self.expr(index).at(prec::CAST, span);
5388 let isize_ty = primitive_ty("isize", span);
5389 if sub {
5390 quote_spanned! {span=> -(#tokens as #isize_ty) }
5391 } else {
5392 quote_spanned! {span=> #tokens as #isize_ty }
5393 }
5394 }
5395
5396 fn call(&mut self, callee: &Callee, args: &[Expr], span: Span) -> Value {
5397 let sig = self.callee_signature(callee);
5398 let reinterpreted = !sig.variadic
5423 && (args.len() != sig.params.len()
5424 || args.iter().zip(&sig.params).any(|(arg, param)| {
5425 arg.ty != *param && !arg.ty.is_error() && !param.is_error()
5426 }));
5427 let promoted: Vec<Ty> = if reinterpreted {
5428 args.iter().map(|arg| arg.ty).collect()
5429 } else {
5430 Vec::new()
5431 };
5432 let params = if reinterpreted {
5433 &promoted
5434 } else {
5435 &sig.params
5436 };
5437
5438 let mut target = match callee {
5439 Callee::Direct(id) => {
5440 let function = self.program.function(*id);
5441 let path = self.function_path(function, span);
5442 if reinterpreted {
5443 let source = self.function_pointer_ty(function, span);
5446 parenthesize(quote_spanned! {span=> #path as #source }, span)
5447 } else {
5448 path
5449 }
5450 }
5451 Callee::Indirect(expr) => {
5452 let value = self.expr(expr).at(prec::CALL, span);
5453 parenthesize(
5456 quote_spanned! {span=> #value.expect("null function pointer") },
5457 span,
5458 )
5459 }
5460 };
5461 if reinterpreted {
5462 let source = self.fn_ptr_ty(&sig.params, sig.variadic, sig.ret, span);
5463 let wanted = self.fn_ptr_ty(params, false, sig.ret, span);
5464 target = parenthesize(
5465 quote_spanned! {span=>
5466 ::core::mem::transmute::<#source, #wanted>(#target)
5467 },
5468 span,
5469 );
5470 }
5471
5472 let mut tokens = self.env_arguments(callee, span);
5476 let hidden = !tokens.is_empty();
5477 for (index, arg) in args.iter().enumerate() {
5478 if index > 0 || hidden {
5479 tokens.extend(quote_spanned! {span=> , });
5480 }
5481 match params.get(index) {
5482 Some(expected) => tokens.extend(self.expr_at(arg, *expected)),
5485 None => {
5491 let arg_span = self.sp(arg.range);
5492 tokens.extend(self.expr(arg).at(prec::LOWEST, arg_span));
5493 }
5494 }
5495 }
5496 let call = parenthesize(tokens, span);
5497 Value::new(quote_spanned! {span=> #target #call }, prec::CALL)
5498 }
5499
5500 fn env_arguments(&self, callee: &Callee, span: Span) -> TokenStream {
5507 let Callee::Direct(id) = callee else {
5508 return TokenStream::new();
5509 };
5510 let mut tokens = TokenStream::new();
5511 for (index, entry) in self.program.function(*id).env.iter().enumerate() {
5512 if index > 0 {
5513 tokens.extend(quote_spanned! {span=> , });
5514 }
5515 match self.env.get(&entry.owner) {
5516 Some(param) => {
5518 let name = self.object_ident(*param, span);
5519 tokens.extend(quote_spanned! {span=> #name });
5520 }
5521 None => {
5523 let object = self.program.object(entry.owner);
5524 let name = self.object_access(entry.owner, span);
5525 tokens.extend(if object.is_const {
5526 quote_spanned! {span=> &raw const #name }
5527 } else {
5528 quote_spanned! {span=> &raw mut #name }
5529 });
5530 }
5531 }
5532 }
5533 tokens
5534 }
5535
5536 fn callee_signature(&self, callee: &Callee) -> ir::Signature {
5539 match callee {
5540 Callee::Direct(id) => self.program.function(*id).sig.clone(),
5541 Callee::Indirect(expr) => match self.program.types.pointee(expr.ty) {
5542 Some(Ty::Func(id)) => {
5543 let func = self.program.types.func_type(id);
5544 ir::Signature {
5545 ret: func.ret,
5546 params: func.params.clone(),
5547 variadic: func.variadic,
5548 prototyped: func.prototyped,
5549 }
5550 }
5551 _ => ir::Signature {
5554 ret: Ty::Void,
5555 params: Vec::new(),
5556 variadic: false,
5557 prototyped: true,
5558 },
5559 },
5560 }
5561 }
5562
5563 fn fn_ptr_ty(&self, params: &[Ty], variadic: bool, ret: Ty, span: Span) -> TokenStream {
5565 let mut list = TokenStream::new();
5566 for (index, param) in params.iter().enumerate() {
5567 if index > 0 {
5568 list.extend(quote_spanned! {span=> , });
5569 }
5570 list.extend(self.ty(*param, span));
5571 }
5572 if variadic {
5573 if !params.is_empty() {
5574 list.extend(quote_spanned! {span=> , });
5575 }
5576 list.extend(quote_spanned! {span=> ... });
5577 }
5578 let list = parenthesize(list, span);
5579 let ret = if ret.is_void() {
5580 TokenStream::new()
5581 } else {
5582 let ty = self.ty(ret, span);
5583 quote_spanned! {span=> -> #ty }
5584 };
5585 quote_spanned! {span=> unsafe extern "C" fn #list #ret }
5586 }
5587
5588 fn function_path(&self, function: &Function, span: Span) -> TokenStream {
5595 let name = self.c_ident(function.item_name(), span);
5596 quote_spanned! {span=> #name }
5597 }
5598
5599 fn function_pointer_ty(&self, function: &Function, span: Span) -> TokenStream {
5602 let sig = &function.sig;
5603 self.fn_ptr_ty(&sig.params, sig.variadic, sig.ret, span)
5604 }
5605
5606 fn condition(&mut self, expr: &Expr) -> Value {
5609 let span = self.sp(expr.range);
5610 match &expr.kind {
5611 ExprKind::Compare { op, lhs, rhs } => {
5612 if let Some(value) = self.null_test(*op, lhs, rhs, span) {
5613 return value;
5614 }
5615 if lhs.ty.is_complex() && rhs.ty.is_complex() {
5616 return self.complex_equality(*op, lhs, rhs, span);
5617 }
5618 let (lhs, rhs) = self.operands(lhs, rhs, BinOp::BitOr);
5619 let left_min = if *op == CmpOp::Lt && lhs.ends_with_type {
5620 prec::CAST + 1
5621 } else {
5622 prec::CMP + 1
5623 };
5624 let ends_with_type = rhs.ends_with_type;
5625 let lhs = lhs.at(left_min, span);
5626 let rhs = rhs.at(prec::CMP + 1, span);
5627 let op = cmp_tokens(*op, span);
5628 Value::new(quote_spanned! {span=> #lhs #op #rhs }, prec::CMP)
5629 .type_end(ends_with_type)
5630 }
5631 ExprKind::Logical { .. } => self.logical_chain(expr),
5632 ExprKind::Int(value) => {
5633 let ident = Ident::new(if *value != 0 { "true" } else { "false" }, span);
5634 Value::atom(quote_spanned! {span=> #ident })
5635 }
5636 _ if expr.ty.is_bool() => self.expr(expr),
5637 _ if expr.ty.is_complex() => self.complex_condition(expr, span),
5638 _ if expr.ty.is_pointer() => self.not_null(expr, span),
5639 _ => {
5640 let ty = expr.ty;
5641 let value = self.expr(expr).at(prec::CMP + 1, span);
5642 let zero = self.zero_tokens(ty, span);
5643 Value::new(quote_spanned! {span=> #value != #zero }, prec::CMP)
5644 }
5645 }
5646 }
5647
5648 fn null_test(&mut self, op: CmpOp, lhs: &Expr, rhs: &Expr, span: Span) -> Option<Value> {
5651 if !matches!(op, CmpOp::Eq | CmpOp::Ne) {
5652 return None;
5653 }
5654 let (pointer, _) = match (&lhs.kind, &rhs.kind) {
5655 (ExprKind::Zeroed, _) if rhs.ty.is_pointer() => (rhs, lhs),
5656 (_, ExprKind::Zeroed) if lhs.ty.is_pointer() => (lhs, rhs),
5657 _ => return None,
5658 };
5659 let test = self.is_null(pointer, span);
5660 if op == CmpOp::Eq {
5661 return Some(test);
5662 }
5663 let tokens = test.at(prec::UNARY, span);
5664 Some(Value::new(quote_spanned! {span=> !#tokens }, prec::UNARY))
5665 }
5666
5667 fn is_null(&mut self, expr: &Expr, span: Span) -> Value {
5669 if self.program.types.is_func_pointer(expr.ty) {
5670 let tokens = self.copied_receiver(expr, span);
5671 return Value::new(quote_spanned! {span=> #tokens.is_none() }, prec::CALL);
5672 }
5673 let tokens = self.expr(expr).at(prec::CALL, span);
5674 Value::new(quote_spanned! {span=> #tokens.is_null() }, prec::CALL)
5675 }
5676
5677 fn not_null(&mut self, expr: &Expr, span: Span) -> Value {
5678 if self.program.types.is_func_pointer(expr.ty) {
5679 let tokens = self.copied_receiver(expr, span);
5680 return Value::new(quote_spanned! {span=> #tokens.is_some() }, prec::CALL);
5681 }
5682 let tokens = self.expr(expr).at(prec::CALL, span);
5683 Value::new(quote_spanned! {span=> !#tokens.is_null() }, prec::UNARY)
5684 }
5685
5686 fn copied_receiver(&mut self, expr: &Expr, span: Span) -> TokenStream {
5693 if self.reads_a_static(expr) {
5694 let tokens = self.expr(expr).at(prec::LOWEST, span);
5695 return braced(tokens, span);
5696 }
5697 self.expr(expr).at(prec::CALL, span)
5698 }
5699
5700 fn reads_a_static(&self, expr: &Expr) -> bool {
5703 let ExprKind::Load(place) = &expr.kind else {
5704 return false;
5705 };
5706 let mut place = place;
5707 loop {
5708 match &place.kind {
5709 PlaceKind::Object(id) => {
5710 let storage = &self.program.object(*id).storage;
5711 return !matches!(storage, Storage::Automatic) && !storage.is_thread_local();
5714 }
5715 PlaceKind::Field { base, .. } => place = base,
5716 _ => return false,
5719 }
5720 }
5721 }
5722
5723 fn operands(&mut self, lhs: &Expr, rhs: &Expr, op: BinOp) -> (Value, Value) {
5732 self.operands_with(None, lhs, rhs, op)
5733 }
5734
5735 fn operands_with(
5741 &mut self,
5742 folded: Option<Value>,
5743 lhs: &Expr,
5744 rhs: &Expr,
5745 op: BinOp,
5746 ) -> (Value, Value) {
5747 let uses_method = matches!(
5748 op,
5749 BinOp::Add | BinOp::Sub | BinOp::Mul | BinOp::Shl | BinOp::Shr
5750 );
5751 let lhs_constant = constant_of(lhs);
5752 let rhs_constant = constant_of(rhs);
5753
5754 let lhs_bare = lhs_constant.is_some() && !uses_method && rhs_constant.is_none();
5757 let lhs_value = match (folded, lhs_constant) {
5758 (Some(value), _) => value,
5759 (None, Some(value)) if lhs_bare => self.bare_value(value, lhs.ty, self.sp(lhs.range)),
5760 (None, _) => self.expr(lhs),
5761 };
5762 let rhs_value = match rhs_constant {
5763 Some(ConstValue::Int(value)) if op.is_shift() => self.bare_value(
5775 ConstValue::Int(i128::from(value as u32)),
5776 Ty::UInt,
5777 self.sp(rhs.range),
5778 ),
5779 Some(value) if op.is_shift() => self.bare_value(value, rhs.ty, self.sp(rhs.range)),
5780 Some(value) if !lhs_bare => self.bare_value(value, rhs.ty, self.sp(rhs.range)),
5781 _ => self.expr(rhs),
5782 };
5783 (lhs_value, rhs_value)
5784 }
5785
5786 fn bare_value(&mut self, value: ConstValue, ty: Ty, span: Span) -> Value {
5789 match value {
5790 ConstValue::Int(v) => {
5791 let tokens = bare_int_literal(v, ty, span);
5792 let mut out = Value::new(tokens, if v < 0 { prec::UNARY } else { prec::ATOM });
5793 out.bare_integer = v >= 0 && !ty.is_bool();
5794 out
5795 }
5796 ConstValue::Float(v) => Value::new(
5797 bare_float_literal(v, span),
5798 if v.is_sign_negative() {
5799 prec::UNARY
5800 } else {
5801 prec::ATOM
5802 },
5803 ),
5804 ConstValue::Complex(re, im) => {
5807 let re = bare_float_literal(re, span);
5808 let im = bare_float_literal(im, span);
5809 self.complex_new(ty, re, im, span)
5810 }
5811 }
5812 }
5813
5814 fn cond_chain(&mut self, expr: &Expr) -> Value {
5828 let mut spine = Vec::new();
5829 let mut node = expr;
5830 while let ExprKind::Cond {
5831 cond,
5832 then_expr,
5833 else_expr,
5834 } = &node.kind
5835 {
5836 let span = self.sp(node.range);
5837 let cond_tokens = self.condition(cond).at_condition(span);
5838 let then_tokens = self.expr(then_expr).at(prec::LOWEST, span);
5839 spine.push((cond_tokens, then_tokens, span));
5840 node = else_expr;
5841 }
5842 let inner = spine
5845 .last()
5846 .map_or_else(|| self.sp(expr.range), |(_, _, span)| *span);
5847 let mut tokens = self.expr(node).at(prec::LOWEST, inner);
5848 while let Some((cond_tokens, then_tokens, span)) = spine.pop() {
5849 tokens = quote_spanned! {span=>
5850 if #cond_tokens { #then_tokens } else { #tokens }
5851 };
5852 }
5853 Value::new(tokens, prec::BLOCK)
5854 }
5855
5856 fn cond_chain_at(&mut self, expr: &Expr, expected: Ty) -> TokenStream {
5862 let mut spine = Vec::new();
5863 let mut node = expr;
5864 while let ExprKind::Cond {
5865 cond,
5866 then_expr,
5867 else_expr,
5868 } = &node.kind
5869 {
5870 if node.ty != expected {
5871 break;
5872 }
5873 let span = self.sp(node.range);
5874 let cond_tokens = self.condition(cond).at_condition(span);
5875 let then_tokens = self.expr_at(then_expr, expected);
5876 spine.push((cond_tokens, then_tokens, span));
5877 node = else_expr;
5878 }
5879 let mut tokens = self.expr_at(node, expected);
5880 while let Some((cond_tokens, then_tokens, span)) = spine.pop() {
5881 tokens = quote_spanned! {span=>
5882 if #cond_tokens { #then_tokens } else { #tokens }
5883 };
5884 }
5885 tokens
5886 }
5887
5888 fn assign_chain(&mut self, expr: &Expr) -> Value {
5898 let mut spine = Vec::new();
5899 let mut node = expr;
5900 while let ExprKind::Assign { place, value } = &node.kind {
5901 let span = self.sp(node.range);
5902 let lowered = self.place(place, true);
5903 spine.push((lowered, self.program.types.unatomic(place.ty), span));
5904 node = value;
5905 }
5906 let (_, innermost, _) = spine
5907 .last()
5908 .expect("assign_chain is only entered on an assignment");
5909 let mut tokens = self.expr_at(node, *innermost);
5910 while let Some((lowered, ty, span)) = spine.pop() {
5911 if lowered.atomic.is_some() {
5916 let tmp = self.temporary();
5917 let target = self.ty(ty, span);
5918 let store = self.write(&lowered, quote_spanned! {span=> #tmp }, span);
5919 let setup = &lowered.setup;
5920 tokens = quote_spanned! {span=>
5921 { #setup let #tmp: #target = #tokens; #store #tmp }
5922 };
5923 continue;
5924 }
5925 let store = self.write(&lowered, tokens, span);
5926 let read = self.read(&lowered, span).at(prec::LOWEST, span);
5930 let setup = &lowered.setup;
5931 tokens = quote_spanned! {span=> { #setup #store #read } };
5932 }
5933 Value::new(tokens, prec::BLOCK)
5934 }
5935
5936 fn binary_chain(&mut self, expr: &Expr) -> Value {
5957 let mut spine = vec![expr];
5958 let mut node = expr;
5959 while let ExprKind::Binary { lhs, .. } = &node.kind {
5960 node = lhs;
5961 if matches!(node.kind, ExprKind::Binary { .. }) {
5962 spine.push(node);
5963 }
5964 }
5965 let mut folded = None;
5966 while let Some(node) = spine.pop() {
5967 let ExprKind::Binary { op, lhs, rhs } = &node.kind else {
5968 unreachable!("the spine holds binary operations only");
5969 };
5970 let span = self.sp(node.range);
5971 let (lhs_value, rhs_value) = self.operands_with(folded, lhs, rhs, *op);
5972 let value = if node.ty.is_complex() {
5973 self.complex_binary(*op, (lhs_value, lhs.ty), (rhs_value, rhs.ty), node.ty, span)
5974 } else {
5975 self.binary(*op, lhs_value, rhs_value, node.ty, span)
5976 };
5977 folded = Some(self.reduce_bits(value, node));
5978 }
5979 folded.expect("the chain has at least the node it started from")
5980 }
5981
5982 fn logical_chain(&mut self, expr: &Expr) -> Value {
5988 let mut spine = vec![expr];
5989 let mut node = expr;
5990 while let ExprKind::Logical { lhs, .. } = &node.kind {
5991 node = lhs;
5992 if matches!(node.kind, ExprKind::Logical { .. }) {
5993 spine.push(node);
5994 }
5995 }
5996 let mut folded = self.condition(node);
6000 while let Some(node) = spine.pop() {
6001 let ExprKind::Logical { op, rhs, .. } = &node.kind else {
6002 unreachable!("the spine holds logical operations only");
6003 };
6004 let span = self.sp(node.range);
6005 let (level, tokens) = match op {
6006 LogicalOp::And => (prec::AND, quote_spanned! {span=> && }),
6007 LogicalOp::Or => (prec::OR, quote_spanned! {span=> || }),
6008 };
6009 let mut out = folded.at(level, span);
6010 let rhs = self.condition(rhs);
6011 let ends_with_type = rhs.ends_with_type;
6012 let rhs = rhs.at(level + 1, span);
6013 out.extend(quote_spanned! {span=> #tokens #rhs });
6014 folded = Value::new(out, level).type_end(ends_with_type);
6015 }
6016 folded
6017 }
6018
6019 fn binary(&mut self, op: BinOp, lhs: Value, rhs: Value, ty: Ty, span: Span) -> Value {
6020 if ty.is_floating() {
6021 let (level, tokens) = match op {
6022 BinOp::Add => (prec::SUM, quote_spanned! {span=> + }),
6023 BinOp::Sub => (prec::SUM, quote_spanned! {span=> - }),
6024 BinOp::Mul => (prec::PRODUCT, quote_spanned! {span=> * }),
6025 BinOp::Div => (prec::PRODUCT, quote_spanned! {span=> / }),
6026 _ => (prec::PRODUCT, quote_spanned! {span=> % }),
6028 };
6029 let ends_with_type = rhs.ends_with_type;
6030 let mut out = lhs.at(level, span);
6031 let rhs = rhs.at(level + 1, span);
6032 out.extend(quote_spanned! {span=> #tokens #rhs });
6037 return Value::new(out, level).type_end(ends_with_type);
6038 }
6039
6040 let method = match op {
6045 BinOp::Add => Some("wrapping_add"),
6046 BinOp::Sub => Some("wrapping_sub"),
6047 BinOp::Mul => Some("wrapping_mul"),
6048 BinOp::Shl => Some("wrapping_shl"),
6049 BinOp::Shr => Some("wrapping_shr"),
6050 _ => None,
6051 };
6052 if let Some(method) = method {
6053 let mut receiver = lhs.at(prec::CALL, span);
6054 let method = Ident::new(method, span);
6055 let argument = if op.is_shift() && !rhs.bare_integer {
6056 let amount = rhs.at(prec::CAST, span);
6059 let amount = if starts_with_minus(&amount) {
6066 parenthesize(amount, span)
6067 } else {
6068 amount
6069 };
6070 let u32_ty = primitive_ty("u32", span);
6071 quote_spanned! {span=> #amount as #u32_ty }
6072 } else {
6073 rhs.at(prec::LOWEST, span)
6074 };
6075 let args = parenthesize(argument, span);
6076 receiver.extend(quote_spanned! {span=> .#method #args });
6077 return Value::new(receiver, prec::CALL);
6078 }
6079
6080 let (level, tokens) = match op {
6084 BinOp::Div => (prec::PRODUCT, quote_spanned! {span=> / }),
6085 BinOp::Rem => (prec::PRODUCT, quote_spanned! {span=> % }),
6086 BinOp::BitAnd => (prec::BIT_AND, quote_spanned! {span=> & }),
6087 BinOp::BitXor => (prec::BIT_XOR, quote_spanned! {span=> ^ }),
6088 BinOp::BitOr => (prec::BIT_OR, quote_spanned! {span=> | }),
6089 _ => unreachable!("every other operator was handled above"),
6090 };
6091 let ends_with_type = rhs.ends_with_type;
6092 let mut out = lhs.at(level, span);
6093 let rhs = rhs.at(level + 1, span);
6094 out.extend(quote_spanned! {span=> #tokens #rhs });
6095 Value::new(out, level).type_end(ends_with_type)
6096 }
6097
6098 fn compound_value(
6103 &mut self,
6104 current: Value,
6105 place_ty: Ty,
6106 op: BinOp,
6107 value: &Expr,
6108 hoisted: Option<Value>,
6109 compute: Ty,
6110 ) -> Value {
6111 let span = self.sp(value.range);
6112 if place_ty.is_pointer() {
6113 let access = current.at(prec::CALL, span);
6115 let offset = match hoisted {
6116 Some(index) => self.offset_of_value(index, op == BinOp::Sub, span),
6117 None => self.offset_argument(value, op == BinOp::Sub, span),
6118 };
6119 let pointee = self.program.types.pointee(place_ty).unwrap_or(Ty::Void);
6120 let offset = match self.vm_scale(pointee, span) {
6121 None => offset,
6122 Some(scale) => quote_spanned! {span=> (#offset).wrapping_mul(#scale) },
6123 };
6124 return Value::new(quote_spanned! {span=> #access.offset(#offset) }, prec::CALL);
6125 }
6126 if compute.is_complex() {
6131 let lhs_ty = if place_ty.is_complex() {
6132 compute
6133 } else {
6134 compute.complex_component()
6135 };
6136 let current = self.cast(current, place_ty, lhs_ty, span);
6137 let rhs = self.compound_operand(value, hoisted, span);
6138 let result = self.complex_binary(op, (current, lhs_ty), (rhs, value.ty), compute, span);
6139 return self.cast(result, compute, place_ty, span);
6140 }
6141 let current = self.cast(current, place_ty, compute, span);
6142 let rhs = self.compound_operand(value, hoisted, span);
6143 let result = self.binary(op, current, rhs, compute, span);
6144 self.cast(result, compute, place_ty, span)
6145 }
6146
6147 fn compound_operand(&mut self, value: &Expr, hoisted: Option<Value>, span: Span) -> Value {
6152 match hoisted {
6153 Some(rhs) => rhs,
6154 None => match constant_of(value) {
6155 Some(constant) => self.bare_value(constant, value.ty, span),
6156 None => self.expr(value),
6157 },
6158 }
6159 }
6160
6161 fn compound_rhs(&mut self, value: &Expr) -> (TokenStream, Option<Value>) {
6176 if !ir::calls_a_function(value) {
6177 return (TokenStream::new(), None);
6178 }
6179 let span = self.sp(value.range);
6180 let tokens = self.expr(value).at(prec::LOWEST, span);
6181 let tmp = self.temporary();
6182 (
6183 quote_spanned! {span=> let #tmp = #tokens; },
6184 Some(Value::atom(quote_spanned! {span=> #tmp })),
6185 )
6186 }
6187
6188 fn offset_of_value(&mut self, index: Value, sub: bool, span: Span) -> TokenStream {
6190 let tokens = index.at(prec::CAST, span);
6191 let isize_ty = primitive_ty("isize", span);
6192 if sub {
6193 quote_spanned! {span=> -(#tokens as #isize_ty) }
6194 } else {
6195 quote_spanned! {span=> #tokens as #isize_ty }
6196 }
6197 }
6198
6199 fn step_value(&mut self, current: Value, ty: Ty, dec: bool, span: Span) -> TokenStream {
6201 if ty.is_pointer() {
6202 let access = current.at(prec::CALL, span);
6203 let pointee = self.program.types.pointee(ty).unwrap_or(Ty::Void);
6204 let one = match (self.vm_scale(pointee, span), dec) {
6205 (None, true) => quote_spanned! {span=> -1 },
6206 (None, false) => quote_spanned! {span=> 1 },
6207 (Some(scale), true) => quote_spanned! {span=> -(#scale) },
6208 (Some(scale), false) => scale,
6209 };
6210 return quote_spanned! {span=> #access.offset(#one) };
6211 }
6212 if ty.is_floating() {
6213 let access = current.at(prec::SUM, span);
6214 let one = Literal::f64_unsuffixed(1.0);
6215 let op = if dec {
6216 quote_spanned! {span=> - }
6217 } else {
6218 quote_spanned! {span=> + }
6219 };
6220 return quote_spanned! {span=> #access #op #one };
6221 }
6222 if ty.is_complex() {
6223 let name = if dec { "sub_real" } else { "add_real" };
6226 let func = self.rt_complex(&format!("{name}_{}", Self::complex_suffix(ty)), span);
6227 let access = current.at(prec::LOWEST, span);
6228 let one = Literal::f64_unsuffixed(1.0);
6229 return quote_spanned! {span=> #func(#access, #one) };
6230 }
6231 if ty.is_bool() {
6232 let access = current.at(prec::CAST, span);
6235 let int = self.ty(Ty::Int, span);
6236 let method = Ident::new(if dec { "wrapping_sub" } else { "wrapping_add" }, span);
6237 return quote_spanned! {span=> (#access as #int).#method(1) != 0 };
6238 }
6239 let access = current.at(prec::CALL, span);
6240 let method = Ident::new(if dec { "wrapping_sub" } else { "wrapping_add" }, span);
6241 quote_spanned! {span=> #access.#method(1) }
6242 }
6243
6244 fn cast(&mut self, value: Value, from: Ty, to: Ty, span: Span) -> Value {
6246 if from == to {
6247 return value;
6248 }
6249 if from.is_complex() || to.is_complex() {
6250 return self.complex_cast(value, from, to, span);
6251 }
6252 let types = &self.program.types;
6253 let from_fn = types.is_func_pointer(from);
6254 let to_fn = types.is_func_pointer(to);
6255 if to.is_bool() {
6256 if from.is_pointer() {
6257 let tokens = value.at(prec::CALL, span);
6260 return if from_fn {
6261 let braced = braced(tokens, span);
6262 Value::new(quote_spanned! {span=> #braced.is_some() }, prec::CALL)
6263 } else {
6264 Value::new(quote_spanned! {span=> !#tokens.is_null() }, prec::UNARY)
6265 };
6266 }
6267 let zero = self.zero_tokens(from, span);
6270 let tokens = value.at(prec::CMP + 1, span);
6271 return Value::new(quote_spanned! {span=> #tokens != #zero }, prec::CMP);
6272 }
6273 if from_fn || to_fn {
6274 let target = self.ty(to, span);
6281 let source = self.ty(from, span);
6282 if from_fn && to_fn && source.to_string() == target.to_string() {
6289 return value;
6290 }
6291 let usize_ty = primitive_ty("usize", span);
6292 if to_fn && !from.is_pointer() {
6293 let tokens = value.at(prec::CAST, span);
6294 return Value::new(
6295 quote_spanned! {span=>
6296 ::core::mem::transmute::<#usize_ty, #target>(#tokens as #usize_ty)
6297 },
6298 prec::CALL,
6299 );
6300 }
6301 if from_fn && !to.is_pointer() {
6302 let tokens = value.at(prec::LOWEST, span);
6303 return Value::new(
6304 quote_spanned! {span=>
6305 ::core::mem::transmute::<#source, #usize_ty>(#tokens) as #target
6306 },
6307 prec::CAST,
6308 )
6309 .type_end(true);
6310 }
6311 let tokens = value.at(prec::LOWEST, span);
6312 return Value::new(
6313 quote_spanned! {span=>
6314 ::core::mem::transmute::<#source, #target>(#tokens)
6315 },
6316 prec::CALL,
6317 );
6318 }
6319 let target = self.ty(to, span);
6320 if from.is_pointer() && to.is_integer() {
6321 let tokens = value.at(prec::CAST, span);
6324 let usize_ty = primitive_ty("usize", span);
6325 return Value::new(
6326 quote_spanned! {span=> #tokens as #usize_ty as #target },
6327 prec::CAST,
6328 )
6329 .type_end(true);
6330 }
6331 if from.is_bool() && to.is_floating() {
6332 let int = self.ty(Ty::Int, span);
6335 let tokens = value.at(prec::CAST, span);
6336 return Value::new(
6337 quote_spanned! {span=> #tokens as #int as #target },
6338 prec::CAST,
6339 )
6340 .type_end(true);
6341 }
6342 let tokens = value.at(prec::CAST, span);
6343 Value::new(quote_spanned! {span=> #tokens as #target }, prec::CAST).type_end(true)
6344 }
6345
6346 fn place(&mut self, place: &Place, mutable: bool) -> LoweredPlace {
6355 let atomic = matches!(place.ty, Ty::Atomic(_));
6361 let mut lowered = self.place_access(place, mutable || atomic);
6362 if lowered.bits.is_none() && self.place_underaligned(place) {
6363 lowered.unaligned = true;
6364 }
6365 if let Ty::Atomic(id) = place.ty {
6366 let inner = self.program.types.atomic_inner(id);
6367 lowered.atomic = ir::atomic_class(&self.program.types, inner, &self.options.target)
6368 .map(|c| (c, inner));
6369 if lowered.atomic.is_some() {
6373 lowered.unaligned = false;
6374 }
6375 }
6376 lowered
6377 }
6378
6379 fn place_underaligned(&self, place: &Place) -> bool {
6389 match &place.kind {
6390 PlaceKind::Deref(_) | PlaceKind::Index { .. } => {
6391 self.place_align(place) < self.type_align(place.ty)
6392 }
6393 PlaceKind::Field { base, .. } => self.place_align(base) < self.type_align(base.ty),
6394 PlaceKind::ComplexPart { .. } => self.place_align(place) < self.type_align(place.ty),
6397 _ => false,
6398 }
6399 }
6400
6401 fn place_align(&self, place: &Place) -> u64 {
6410 match &place.kind {
6411 PlaceKind::Deref(ptr) => self.pointer_align(ptr),
6412 PlaceKind::Index { base, .. } => {
6413 self.pointer_align(base).min(self.type_align(place.ty))
6418 }
6419 PlaceKind::Field {
6420 base,
6421 record,
6422 index,
6423 } => {
6424 let base_align = self.place_align(base);
6425 let offset = self.program.types.record(*record).fields[*index].offset;
6426 if offset == 0 {
6427 base_align
6428 } else {
6429 base_align.min(1 << offset.trailing_zeros())
6430 }
6431 }
6432 PlaceKind::ComplexPart { base, imag } => {
6435 let base_align = self.place_align(base);
6436 if *imag {
6437 base_align.min(place.ty.size_bytes(&self.options.target).max(1))
6438 } else {
6439 base_align
6440 }
6441 }
6442 PlaceKind::Object(id) => self
6445 .object_align(*id)
6446 .unwrap_or(1)
6447 .max(self.type_align(place.ty)),
6448 _ => self.type_align(place.ty),
6449 }
6450 }
6451
6452 fn pointer_align(&self, ptr: &Expr) -> u64 {
6454 match &ptr.kind {
6455 ExprKind::AddrOf(place) => self.place_align(place),
6456 ExprKind::Cast(inner) if inner.ty.is_pointer() || inner.ty.is_array() => {
6459 self.pointer_align(inner)
6460 }
6461 ExprKind::PtrOffset { ptr, .. } => self
6462 .pointer_align(ptr)
6463 .min(self.pointee_align(ptr.ty).unwrap_or(1)),
6464 _ => self.pointee_align(ptr.ty).unwrap_or(u64::MAX),
6465 }
6466 }
6467
6468 fn pointee_align(&self, ty: Ty) -> Option<u64> {
6470 let pointee = self.program.types.pointee(ty)?;
6471 (!pointee.is_void() && !pointee.is_func()).then(|| self.type_align(pointee))
6472 }
6473
6474 fn type_align(&self, ty: Ty) -> u64 {
6477 match ty {
6478 Ty::Record(id) => self.program.types.record(id).rust_align,
6481 Ty::Array(id) => self.type_align(self.program.types.array_type(id).elem),
6482 _ => self
6483 .program
6484 .types
6485 .size_align(ty, &self.options.target)
6486 .map_or(1, |layout| layout.align),
6487 }
6488 }
6489
6490 fn place_access(&mut self, place: &Place, mutable: bool) -> LoweredPlace {
6491 let span = self.sp(place.range);
6492 match &place.kind {
6493 PlaceKind::Object(id) if self.program.object(*id).storage.is_thread_local() => {
6494 let name = self.object_ident(*id, span);
6501 let tmp = self.temporary_at(span);
6502 let cell = Ident::new("__cinrs_cell", Span::mixed_site());
6503 let object = parenthesize(quote_spanned! {span=> *#tmp }, span);
6507 let object = self.through_storage(*id, object, span);
6508 LoweredPlace::plain(
6509 quote_spanned! {span=>
6510 let #tmp = #name.with(|#cell| ::core::cell::UnsafeCell::get(#cell));
6511 },
6512 object,
6513 )
6514 }
6515 PlaceKind::Object(id) => {
6516 let access = self.object_access(*id, span);
6517 LoweredPlace::plain(TokenStream::new(), access)
6518 }
6519 PlaceKind::Deref(ptr) => self.deref_place(ptr, place.ty, mutable, span),
6520 PlaceKind::Index { base, index } => {
6521 let pointer = self.pointer_operand(base, place.ty, mutable, span);
6522 let offset = self.scaled_offset(place.ty, index, false, span);
6523 let tmp = self.temporary_at(span);
6524 LoweredPlace::plain(
6525 quote_spanned! {span=> let #tmp = #pointer.offset(#offset); },
6526 parenthesize(quote_spanned! {span=> *#tmp }, span),
6527 )
6528 }
6529 PlaceKind::Field {
6530 base,
6531 record,
6532 index,
6533 } => {
6534 let field = self.program.types.record(*record).fields[*index].clone();
6535 let lowered = self.place(base, mutable);
6536 let access = lowered.access;
6537 let Some(bits) = &field.bits else {
6538 let name = self.c_ident(&field.name, span);
6539 return LoweredPlace::plain(
6540 lowered.setup,
6541 quote_spanned! {span=> #access.#name },
6542 );
6543 };
6544 let access = if rooted_in_static(base, self.program) {
6548 parenthesize(quote_spanned! {span=> *(&raw mut #access) }, span)
6549 } else {
6550 access
6551 };
6552 LoweredPlace {
6553 setup: lowered.setup,
6554 access,
6555 bits: Some(BitAccess {
6556 getter: self.c_ident(&bits.getter, span),
6557 setter: self.c_ident(&bits.setter, span),
6558 }),
6559 unaligned: false,
6560 atomic: None,
6561 }
6562 }
6563 PlaceKind::ComplexPart { base, imag } => {
6567 let lowered = self.place(base, mutable);
6568 let access = lowered.access;
6569 let field = Ident::new(if *imag { "im" } else { "re" }, span);
6570 LoweredPlace::plain(lowered.setup, quote_spanned! {span=> #access.#field })
6571 }
6572 PlaceKind::Str(id) => {
6573 let pointer = self.string_pointer(*id, !mutable, span);
6574 let tmp = self.temporary_at(span);
6575 LoweredPlace::plain(
6576 quote_spanned! {span=> let #tmp = #pointer; },
6577 parenthesize(quote_spanned! {span=> *#tmp }, span),
6578 )
6579 }
6580 PlaceKind::Temporary(expr) => {
6581 let ty = expr.ty;
6582 let value = self.expr_at(expr, ty);
6583 let tmp = self.temporary();
6584 LoweredPlace::plain(
6585 quote_spanned! {span=> let mut #tmp = #value; },
6586 quote_spanned! {span=> #tmp },
6587 )
6588 }
6589 PlaceKind::CompoundLiteral { object, init } => {
6596 let name = self.object_ident(*object, span);
6597 let value = self.expr_at(init, place.ty);
6598 LoweredPlace::plain(
6599 quote_spanned! {span=> #name = #value; },
6600 quote_spanned! {span=> #name },
6601 )
6602 }
6603 }
6604 }
6605
6606 fn read(&self, place: &LoweredPlace, span: Span) -> Value {
6608 let access = &place.access;
6609 if let Some((class, ty)) = place.atomic {
6610 let object = self.atomic_object_of(place, span);
6611 let order = self.ordering(ir::MemOrder::SeqCst, span);
6612 return self.repr_to_value(
6613 class,
6614 ty,
6615 quote_spanned! {span=> #object.load(#order) },
6616 span,
6617 );
6618 }
6619 match &place.bits {
6620 Some(bits) => {
6621 let getter = &bits.getter;
6622 Value::new(quote_spanned! {span=> #access.#getter() }, prec::CALL)
6623 }
6624 None if place.unaligned => Value::new(
6625 quote_spanned! {span=> (&raw const #access).read_unaligned() },
6626 prec::CALL,
6627 ),
6628 None => Value::atom(access.clone()),
6629 }
6630 }
6631
6632 fn write(&self, place: &LoweredPlace, value: TokenStream, span: Span) -> TokenStream {
6634 let access = &place.access;
6635 if let Some((class, _)) = place.atomic {
6636 let object = self.atomic_object_of(place, span);
6637 let order = self.ordering(ir::MemOrder::SeqCst, span);
6638 let value = self.value_to_repr(class, Value::new(value, prec::LOWEST), span);
6639 return quote_spanned! {span=> #object.store(#value, #order); };
6640 }
6641 match &place.bits {
6642 Some(bits) => {
6643 let setter = &bits.setter;
6644 quote_spanned! {span=> #access.#setter(#value); }
6645 }
6646 None if place.unaligned => {
6647 quote_spanned! {span=> (&raw mut #access).write_unaligned(#value); }
6648 }
6649 None => quote_spanned! {span=> #access = #value; },
6650 }
6651 }
6652
6653 fn atomic_object_of(&self, place: &LoweredPlace, span: Span) -> TokenStream {
6659 let access = &place.access;
6660 let class = place.atomic.expect("an atomic place").0;
6661 self.atomic_ref(class, quote_spanned! {span=> (&raw mut #access) }, span)
6662 }
6663
6664 fn deref_place(&mut self, ptr: &Expr, pointee: Ty, mutable: bool, span: Span) -> LoweredPlace {
6666 let simple =
6667 matches!(&ptr.kind, ExprKind::Load(p) if matches!(p.kind, PlaceKind::Object(_)));
6668 if simple {
6669 let tokens = self.pointer_operand(ptr, pointee, mutable, span);
6672 return LoweredPlace::plain(
6673 TokenStream::new(),
6674 parenthesize(quote_spanned! {span=> *#tokens }, span),
6675 );
6676 }
6677 let value = self.pointer_operand(ptr, pointee, mutable, span);
6678 let tmp = self.temporary_at(span);
6679 LoweredPlace::plain(
6680 quote_spanned! {span=> let #tmp = #value; },
6681 parenthesize(quote_spanned! {span=> *#tmp }, span),
6682 )
6683 }
6684
6685 fn pointer_operand(
6692 &mut self,
6693 ptr: &Expr,
6694 pointee: Ty,
6695 mutable: bool,
6696 span: Span,
6697 ) -> TokenStream {
6698 if mutable && self.program.types.points_to_const(ptr.ty) {
6699 let target = self.pointee_ty(pointee, span);
6700 let tokens = self.expr(ptr).at(prec::CAST, span);
6701 return parenthesize(quote_spanned! {span=> #tokens as *mut #target }, span);
6702 }
6703 self.expr(ptr).at(prec::CALL, span)
6704 }
6705
6706 fn address_of(&mut self, place: &Place, want: Ty, span: Span) -> Value {
6708 if let PlaceKind::Object(id) = &place.kind
6712 && self.program.types.is_vm(place.ty)
6713 {
6714 let name = self.object_ident(*id, span);
6715 let value = Value::atom(quote_spanned! {span=> #name });
6716 let elem = self.program.types.elem(place.ty);
6717 let natural = self.program.types.pointee(want) == elem
6718 && !self.program.types.points_to_const(want);
6719 if natural {
6720 return value;
6721 }
6722 let target = self.ty(want, span);
6723 let tokens = value.at(prec::CAST, span);
6724 return Value::new(quote_spanned! {span=> #tokens as #target }, prec::CAST)
6725 .type_end(true);
6726 }
6727 match &place.kind {
6730 PlaceKind::Deref(ptr) => {
6731 let from = ptr.ty;
6732 let value = self.expr(ptr);
6733 return self.pointer_cast(value, from, want, span);
6734 }
6735 PlaceKind::Index { base, index } => {
6736 let from = base.ty;
6737 let pointer = self.expr(base).at(prec::CALL, span);
6738 let offset = self.scaled_offset(place.ty, index, false, span);
6739 let value = Value::new(
6740 quote_spanned! {span=> #pointer.offset(#offset) },
6741 prec::CALL,
6742 );
6743 return self.pointer_cast(value, from, want, span);
6744 }
6745 PlaceKind::Str(id) => {
6746 let konst = self.program.types.points_to_const(want);
6747 let tokens = self.string_pointer(*id, konst, span);
6748 return Value::new(tokens, prec::CALL);
6749 }
6750 _ => {}
6751 }
6752 let lowered = self.place(place, true);
6753 let access = lowered.access;
6754 let address = quote_spanned! {span=> &raw mut #access };
6755 let natural = Value::new(parenthesize(address, span), prec::ATOM);
6756 let value = self.array_or_pointer_cast(natural, place.ty, want, span);
6757 if lowered.setup.is_empty() {
6758 return value;
6759 }
6760 let setup = lowered.setup;
6761 let tokens = value.at(prec::LOWEST, span);
6762 Value::new(quote_spanned! {span=> { #setup #tokens } }, prec::BLOCK)
6763 }
6764
6765 fn array_or_pointer_cast(&mut self, value: Value, from: Ty, want: Ty, span: Span) -> Value {
6767 if let Ty::Array(id) = from {
6768 let array = self.program.types.array_type(id);
6772 let wanted_pointee = self.program.types.pointee(want);
6773 if wanted_pointee == Some(array.elem) {
6774 let elem = self.pointee_ty(array.elem, span);
6775 let tokens = value.at(prec::CALL, span);
6776 let cast = Value::new(quote_spanned! {span=> #tokens.cast::<#elem>() }, prec::CALL);
6777 return self.constify(cast, want, span);
6778 }
6779 }
6780 let natural_mut = matches!(self.program.types.pointee(want), Some(pointee) if pointee == from)
6781 && !self.program.types.points_to_const(want);
6782 if natural_mut {
6783 return value;
6784 }
6785 let target = self.ty(want, span);
6786 let tokens = value.at(prec::CAST, span);
6787 Value::new(quote_spanned! {span=> #tokens as #target }, prec::CAST).type_end(true)
6788 }
6789
6790 fn constify(&mut self, value: Value, want: Ty, span: Span) -> Value {
6792 if !self.program.types.points_to_const(want) {
6793 return value;
6794 }
6795 let target = self.ty(want, span);
6796 let tokens = value.at(prec::CAST, span);
6797 Value::new(quote_spanned! {span=> #tokens as #target }, prec::CAST).type_end(true)
6798 }
6799
6800 fn pointer_cast(&mut self, value: Value, from: Ty, want: Ty, span: Span) -> Value {
6802 if from == want {
6803 return value;
6804 }
6805 let target = self.ty(want, span);
6806 let tokens = value.at(prec::CAST, span);
6807 Value::new(quote_spanned! {span=> #tokens as #target }, prec::CAST).type_end(true)
6808 }
6809
6810 fn string_pointer(&mut self, id: ir::StrId, konst: bool, span: Span) -> TokenStream {
6812 let data = self.program.string(id);
6813 let element = data.elem;
6814 if element.size_bytes(&self.options.target) > 1 {
6815 let elem = self.ty(element, span);
6819 let mut items = TokenStream::new();
6820 for value in data.values.iter().chain(std::iter::once(&0)) {
6821 let literal =
6822 int_literal_token(element.wrap(i128::from(*value), &self.options.target), span);
6823 items.extend(quote_spanned! {span=> #literal, });
6824 }
6825 let len = usize_literal(data.len_with_nul(), span);
6826 let name = Ident::new("__CINRS_WIDE", Span::mixed_site());
6827 let array = bracketed(items, span);
6828 let ty = bracketed(quote_spanned! {span=> #elem ; #len }, span);
6829 let pointer = if konst {
6830 quote_spanned! {span=> (&raw const #name).cast::<#elem>() }
6831 } else {
6832 quote_spanned! {span=> (&raw const #name).cast::<#elem>().cast_mut() }
6833 };
6834 return quote_spanned! {span=>
6835 { static #name: #ty = #array; #pointer }
6836 };
6837 }
6838 let mut literal = Literal::byte_string(&nul_terminated(&data.values));
6841 literal.set_span(span);
6842 let elem = self.ty(element, span);
6843 if konst {
6844 quote_spanned! {span=> #literal.as_ptr().cast::<#elem>() }
6845 } else {
6846 quote_spanned! {span=> #literal.as_ptr().cast::<#elem>().cast_mut() }
6847 }
6848 }
6849
6850 fn int_literal(&self, value: i128, ty: Ty, span: Span) -> Value {
6853 if ty.is_bool() {
6854 return Value::atom(bare_int_literal(value, ty, span));
6855 }
6856 if ty == Ty::UInt128 {
6857 let literal = u128_literal_token(value as u128, span);
6860 let target = self.ty(ty, span);
6861 return Value::new(quote_spanned! {span=> #literal as #target }, prec::CAST)
6862 .type_end(true);
6863 }
6864 let literal = int_literal_token(value, span);
6865 let target = self.ty(ty, span);
6866 Value::new(quote_spanned! {span=> #literal as #target }, prec::CAST).type_end(true)
6867 }
6868
6869 fn float_literal(&self, value: f64, ty: Ty, span: Span) -> Value {
6870 if !value.is_finite() {
6871 return Value::new(self.non_finite_literal(value, ty, span), prec::CAST).type_end(true);
6872 }
6873 let literal = float_literal_token(value, span);
6874 let target = self.ty(ty, span);
6875 Value::new(quote_spanned! {span=> #literal as #target }, prec::CAST).type_end(true)
6876 }
6877
6878 fn non_finite_literal(&self, value: f64, ty: Ty, span: Span) -> TokenStream {
6885 let target = self.ty(ty, span);
6886 let f64_ty = primitive_ty("f64", span);
6887 if value.is_nan() {
6888 let bits = value.to_bits();
6889 if bits == f64::NAN.to_bits() {
6890 return quote_spanned! {span=> <#f64_ty>::NAN as #target };
6891 }
6892 if ty == Ty::Float {
6893 let f32_ty = primitive_ty("f32", span);
6894 let literal =
6895 unsigned_hex_literal(u64::from(ir::narrow_nan_bits(bits)), "u32", span);
6896 return quote_spanned! {span=> <#f32_ty>::from_bits(#literal) };
6897 }
6898 let literal = unsigned_hex_literal(bits, "u64", span);
6899 return quote_spanned! {span=> <#f64_ty>::from_bits(#literal) as #target };
6900 }
6901 if value.is_sign_negative() {
6902 quote_spanned! {span=> -<#f64_ty>::INFINITY as #target }
6903 } else {
6904 quote_spanned! {span=> <#f64_ty>::INFINITY as #target }
6905 }
6906 }
6907
6908 fn zero_tokens(&self, ty: Ty, span: Span) -> TokenStream {
6910 match ty {
6911 Ty::Atomic(id) => self.zero_tokens(self.program.types.atomic_inner(id), span),
6914 _ if ty.is_complex() => {
6915 let zero = bare_float_literal(0.0, span);
6916 self.complex_new(ty, zero.clone(), zero, span)
6917 .at(prec::LOWEST, span)
6918 }
6919 _ if ty.is_floating() => bare_float_literal(0.0, span),
6920 _ if ty.is_integer() => bare_int_literal(0, ty, span),
6921 Ty::Array(_) if self.program.types.is_vm(ty) => {
6925 let step = self.ty(self.program.types.vm_step_ty(ty), span);
6926 quote_spanned! {span=> ::core::ptr::null_mut::<#step>() }
6927 }
6928 Ty::Pointer(id) => {
6929 let pointer = self.program.types.pointer_type(id);
6930 if let Ty::Func(func) = pointer.pointee {
6931 let signature = self.fn_ty(func, span);
6936 return quote_spanned! {span=>
6937 ::core::option::Option::<#signature>::None
6938 };
6939 }
6940 let pointee = self.pointee_ty(pointer.pointee, span);
6941 if pointer.konst {
6942 quote_spanned! {span=> ::core::ptr::null::<#pointee>() }
6943 } else {
6944 quote_spanned! {span=> ::core::ptr::null_mut::<#pointee>() }
6945 }
6946 }
6947 other => {
6948 let target = self.ty(other, span);
6951 quote_spanned! {span=> ::core::mem::zeroed::<#target>() }
6952 }
6953 }
6954 }
6955}
6956
6957fn constant_bits(expr: &Expr) -> Option<i128> {
6962 match &expr.kind {
6963 ExprKind::Int(value) => Some(*value),
6964 ExprKind::Zeroed if expr.ty.is_integer() => Some(0),
6965 _ => None,
6966 }
6967}
6968
6969fn pack_bits(storage: &mut [u8], bits: &ir::BitField, value: i128) {
6971 let start = bits.offset_in_storage();
6972 for bit in 0..u64::from(bits.width) {
6973 if (value as u128) >> bit & 1 == 0 {
6974 continue;
6975 }
6976 let at = start + bit;
6977 if let Some(byte) = storage.get_mut((at / 8) as usize) {
6978 *byte |= 1 << (at % 8);
6979 }
6980 }
6981}
6982
6983fn byte_array(bytes: &[u8], span: Span) -> TokenStream {
6985 if bytes.iter().all(|byte| *byte == 0) {
6986 let len = usize_literal(bytes.len() as u64, span);
6987 return bracketed(quote_spanned! {span=> 0; #len }, span);
6988 }
6989 let mut items = TokenStream::new();
6990 for byte in bytes {
6991 let value = hex_literal(u64::from(*byte), span);
6992 items.extend(quote_spanned! {span=> #value, });
6993 }
6994 bracketed(items, span)
6995}
6996
6997fn rooted_in_static(place: &Place, program: &Program) -> bool {
7008 let mut place = place;
7009 loop {
7010 match &place.kind {
7011 PlaceKind::Object(id) => {
7012 let storage = &program.object(*id).storage;
7013 return !matches!(storage, Storage::Automatic) && !storage.is_thread_local();
7014 }
7015 PlaceKind::Field { base, .. } => place = base,
7016 _ => return false,
7017 }
7018 }
7019}
7020
7021fn unsigned_rust_ty(width: u32, span: Span) -> TokenStream {
7024 primitive_ty(
7025 match width {
7026 0..=8 => "u8",
7027 9..=16 => "u16",
7028 17..=32 => "u32",
7029 _ => "u64",
7030 },
7031 span,
7032 )
7033}
7034
7035fn signed_rust_ty(width: u32, span: Span) -> TokenStream {
7037 primitive_ty(
7038 match width {
7039 0..=8 => "i8",
7040 9..=16 => "i16",
7041 17..=32 => "i32",
7042 _ => "i64",
7043 },
7044 span,
7045 )
7046}
7047
7048fn mask_of(width: u32, word_bits: u32) -> u128 {
7050 let width = width.min(word_bits);
7051 if width >= 128 {
7052 u128::MAX
7053 } else {
7054 (1u128 << width) - 1
7055 }
7056}
7057
7058fn word_literal(value: u128, word_bits: u32, span: Span) -> TokenStream {
7065 if word_bits <= 64 {
7066 return hex_literal(value as u64, span);
7067 }
7068 let mut literal = Literal::from_str(&format!("0x{value:x}u128"))
7069 .unwrap_or_else(|_| Literal::u128_suffixed(value));
7070 literal.set_span(span);
7071 TokenStream::from(TokenTree::Literal(literal))
7072}
7073
7074fn hex_literal(value: u64, span: Span) -> TokenStream {
7076 let mut literal = Literal::from_str(&format!("0x{value:x}"))
7077 .unwrap_or_else(|_| Literal::u64_unsuffixed(value));
7078 literal.set_span(span);
7079 TokenStream::from(TokenTree::Literal(literal))
7080}
7081
7082fn state_literal(value: usize, span: Span) -> TokenStream {
7084 let mut literal = Literal::u32_unsuffixed(value as u32);
7085 literal.set_span(span);
7086 TokenStream::from(TokenTree::Literal(literal))
7087}
7088
7089fn group_cases(cases: &[(ir::CaseRange, BlockId)]) -> Vec<(BlockId, Vec<ir::CaseRange>)> {
7094 let mut out: Vec<(BlockId, Vec<ir::CaseRange>)> = Vec::new();
7095 for (value, target) in cases {
7096 match out.iter_mut().find(|(block, _)| block == target) {
7097 Some((_, values)) => values.push(*value),
7098 None => out.push((*target, vec![*value])),
7099 }
7100 }
7101 out
7102}
7103
7104fn case_pattern(value: ir::CaseRange, ty: Ty, span: Span) -> TokenStream {
7106 let low = bare_int_literal(value.low, ty, span);
7107 if value.is_single() {
7108 return low;
7109 }
7110 let high = bare_int_literal(value.high, ty, span);
7111 quote_spanned! {span=> #low ..= #high }
7112}
7113
7114fn zero_prec(ty: Ty) -> u8 {
7116 if ty.is_arithmetic() {
7117 prec::ATOM
7118 } else {
7119 prec::CALL
7120 }
7121}
7122
7123fn link_name(symbol: &str, span: Span) -> TokenStream {
7125 let mut literal = Literal::string(symbol);
7126 literal.set_span(span);
7127 quote_spanned! {span=> #[link_name = #literal] }
7128}
7129
7130fn export_attr(symbol: &str, item: &Ident, span: Span) -> TokenStream {
7140 if item.to_string().trim_start_matches("r#") == symbol {
7141 return quote_spanned! {span=> #[unsafe(no_mangle)] };
7142 }
7143 let mut literal = Literal::string(symbol);
7144 literal.set_span(span);
7145 quote_spanned! {span=> #[unsafe(export_name = #literal)] }
7146}
7147
7148fn needs_unsafe(types: &ir::Types, expr: &Expr) -> bool {
7155 let recurse = |inner| needs_unsafe(types, inner);
7156 match &expr.kind {
7157 ExprKind::Zeroed => !expr.ty.is_scalar(),
7159 ExprKind::AddrOf(place) => !matches!(place.kind, PlaceKind::Str(_)),
7162 ExprKind::Cast(inner) => {
7163 let transmuted = types.is_func_pointer(expr.ty) || types.is_func_pointer(inner.ty);
7164 transmuted || recurse(inner)
7165 }
7166 ExprKind::PtrOffset { .. } => true,
7170 ExprKind::ComplexOf { re, im } => recurse(re) || recurse(im),
7171 ExprKind::RecordLit { fields, .. } => fields.iter().any(recurse),
7172 ExprKind::UnionLit { value, .. } => recurse(value),
7173 ExprKind::ArrayLit(items) => items.iter().any(recurse),
7174 ExprKind::ArrayRepeat { value, .. } => recurse(value),
7175 _ => false,
7176 }
7177}
7178
7179fn nul_terminated(values: &[u32]) -> Vec<u8> {
7181 let mut bytes: Vec<u8> = values.iter().map(|v| *v as u8).collect();
7182 bytes.push(0);
7183 bytes
7184}
7185
7186const UNSUFFIXED_LIMIT: i128 = i32::MAX as i128;
7193
7194fn u128_literal_token(value: u128, span: Span) -> TokenStream {
7197 let mut literal = Literal::u128_suffixed(value);
7198 literal.set_span(span);
7199 TokenStream::from(TokenTree::Literal(literal))
7200}
7201
7202fn int_literal_token(value: i128, span: Span) -> TokenStream {
7204 if value == i128::MIN {
7205 let mut literal = Literal::from_str("170141183460469231731687303715884105728i128")
7209 .expect("a decimal literal followed by a suffix is a token");
7210 literal.set_span(span);
7211 return quote_spanned! {span=> -#literal };
7212 }
7213 let magnitude = value.unsigned_abs();
7214 let mut literal = if magnitude <= UNSUFFIXED_LIMIT as u128 {
7215 Literal::u128_unsuffixed(magnitude)
7216 } else if value >= 0 {
7217 if magnitude <= u32::MAX as u128 {
7218 Literal::u32_suffixed(magnitude as u32)
7219 } else if magnitude <= u64::MAX as u128 {
7220 Literal::u64_suffixed(magnitude as u64)
7221 } else {
7222 Literal::u128_suffixed(magnitude)
7223 }
7224 } else if magnitude <= i64::MAX as u128 {
7225 Literal::i64_suffixed(magnitude as i64)
7226 } else {
7227 Literal::i128_suffixed(magnitude as i128)
7228 };
7229 literal.set_span(span);
7230 if value < 0 {
7231 quote_spanned! {span=> -#literal }
7232 } else {
7233 TokenStream::from(TokenTree::Literal(literal))
7234 }
7235}
7236
7237fn bare_int_literal(value: i128, ty: Ty, span: Span) -> TokenStream {
7240 if ty.is_bool() {
7241 let ident = Ident::new(if value != 0 { "true" } else { "false" }, span);
7242 return quote_spanned! {span=> #ident };
7243 }
7244 if ty == Ty::UInt128 {
7245 let mut literal = Literal::u128_unsuffixed(value as u128);
7248 literal.set_span(span);
7249 return TokenStream::from(TokenTree::Literal(literal));
7250 }
7251 let mut literal = Literal::u128_unsuffixed(value.unsigned_abs());
7252 literal.set_span(span);
7253 if value < 0 {
7254 quote_spanned! {span=> -#literal }
7255 } else {
7256 TokenStream::from(TokenTree::Literal(literal))
7257 }
7258}
7259
7260fn window_ty(word_bits: u32, signed: bool, span: Span) -> TokenStream {
7266 match (word_bits, signed) {
7267 (128, false) => primitive_ty("u128", span),
7268 (128, true) => primitive_ty("i128", span),
7269 (_, false) => primitive_ty("u64", span),
7270 (_, true) => primitive_ty("i64", span),
7271 }
7272}
7273
7274fn primitive_ty(name: &str, span: Span) -> TokenStream {
7291 let ident = Ident::new(name, span);
7292 quote_spanned! {span=> ::core::primitive::#ident }
7293}
7294
7295fn respan(tokens: TokenStream, span: Span) -> TokenStream {
7303 tokens
7304 .into_iter()
7305 .map(|tree| {
7306 let mut tree = match tree {
7307 TokenTree::Group(group) => {
7308 TokenTree::Group(Group::new(group.delimiter(), respan(group.stream(), span)))
7309 }
7310 other => other,
7311 };
7312 tree.set_span(span);
7313 tree
7314 })
7315 .collect()
7316}
7317
7318fn message_literal(text: &str, span: Span) -> TokenStream {
7321 let mut literal = Literal::string(text);
7322 literal.set_span(span);
7323 TokenStream::from(TokenTree::Literal(literal))
7324}
7325
7326fn usize_literal(value: u64, span: Span) -> TokenStream {
7328 let mut literal = Literal::usize_unsuffixed(value as usize);
7329 literal.set_span(span);
7330 TokenStream::from(TokenTree::Literal(literal))
7331}
7332
7333fn float_bit_ty(ty: Ty, span: Span) -> (TokenStream, TokenStream) {
7338 if ty == Ty::Float {
7339 return (
7340 primitive_ty("f32", span),
7341 unsigned_hex_literal(0x7fff_ffff, "u32", span),
7342 );
7343 }
7344 (
7345 primitive_ty("f64", span),
7346 unsigned_hex_literal(0x7fff_ffff_ffff_ffff, "u64", span),
7347 )
7348}
7349
7350fn quiet_bit_literal(ty: Ty, span: Span) -> TokenStream {
7353 if ty == Ty::Float {
7354 return unsigned_hex_literal(1 << 22, "u32", span);
7355 }
7356 unsigned_hex_literal(1 << 51, "u64", span)
7357}
7358
7359fn unsigned_hex_literal(value: u64, suffix: &str, span: Span) -> TokenStream {
7361 let mut literal = Literal::from_str(&format!("0x{value:x}{suffix}"))
7362 .expect("a hexadecimal literal followed by a suffix is a token");
7363 literal.set_span(span);
7364 TokenStream::from(TokenTree::Literal(literal))
7365}
7366
7367fn float_literal_token(value: f64, span: Span) -> TokenStream {
7368 let mut literal = Literal::f64_unsuffixed(value.abs());
7369 literal.set_span(span);
7370 if value.is_sign_negative() {
7371 quote_spanned! {span=> -#literal }
7372 } else {
7373 TokenStream::from(TokenTree::Literal(literal))
7374 }
7375}
7376
7377fn bare_float_literal(value: f64, span: Span) -> TokenStream {
7378 float_literal_token(value, span)
7379}
7380
7381fn comma_operands(expr: &Expr) -> Vec<&Expr> {
7392 let mut out = Vec::new();
7393 let mut node = expr;
7394 while let ExprKind::Comma { lhs, rhs } = &node.kind {
7395 out.push(&**rhs);
7396 node = lhs;
7397 }
7398 out.push(node);
7399 out.reverse();
7400 out
7401}
7402
7403fn constant_of(expr: &Expr) -> Option<ConstValue> {
7404 match &expr.kind {
7405 ExprKind::Int(value) => Some(ConstValue::Int(*value)),
7406 ExprKind::Float(value) if value.is_finite() => Some(ConstValue::Float(*value)),
7407 _ => None,
7408 }
7409}
7410
7411fn cmp_tokens(op: CmpOp, span: Span) -> TokenStream {
7412 match op {
7413 CmpOp::Lt => quote_spanned! {span=> < },
7414 CmpOp::Gt => quote_spanned! {span=> > },
7415 CmpOp::Le => quote_spanned! {span=> <= },
7416 CmpOp::Ge => quote_spanned! {span=> >= },
7417 CmpOp::Eq => quote_spanned! {span=> == },
7418 CmpOp::Ne => quote_spanned! {span=> != },
7419 }
7420}
7421
7422#[cfg(test)]
7423mod tests {
7424 use super::*;
7425
7426 fn spellings(names: &[&str]) -> Vec<(String, String)> {
7429 let mut pairs: Vec<(String, String)> = unique_spellings(names.iter().copied())
7430 .into_iter()
7431 .collect();
7432 pairs.sort();
7433 pairs
7434 }
7435
7436 fn assert_spellings(names: &[&str], expected: &[(&str, &str)]) {
7439 let renamed = unique_spellings(names.iter().copied());
7440 let want: Vec<(String, String)> = expected
7441 .iter()
7442 .map(|(c, rust)| ((*c).to_owned(), (*rust).to_owned()))
7443 .collect();
7444 assert_eq!(spellings(names), want);
7445 let mut seen: HashMap<String, &str> = HashMap::new();
7446 for name in names {
7447 let spelling = match renamed.get(*name) {
7448 Some(unique) => unique.clone(),
7449 None => plain_spelling(name),
7450 };
7451 if let Some(other) = seen.insert(spelling.clone(), name) {
7452 assert_eq!(
7453 other, *name,
7454 "{other} and {name} are both spelled {spelling}"
7455 );
7456 }
7457 }
7458 }
7459
7460 #[test]
7461 fn a_name_rust_can_spell_keeps_it() {
7462 assert_spellings(&["counter", "match", "loop", "café"], &[]);
7466 }
7467
7468 #[test]
7469 fn the_five_names_that_cannot_be_raw_grow_an_underscore() {
7470 assert_spellings(
7471 &["self", "Self", "super", "crate", "_"],
7472 &[
7473 ("Self", "Self_"),
7474 ("_", "__"),
7475 ("crate", "crate_"),
7476 ("self", "self_"),
7477 ("super", "super_"),
7478 ],
7479 );
7480 }
7481
7482 #[test]
7483 fn a_dollar_is_spelled_out() {
7484 assert_spellings(
7485 &["a$b", "$", "x$"],
7486 &[
7487 ("$", "_dollar_"),
7488 ("a$b", "a_dollar_b"),
7489 ("x$", "x_dollar_"),
7490 ],
7491 );
7492 }
7493
7494 #[test]
7495 fn a_spelling_the_program_already_uses_grows_another_underscore() {
7496 assert_spellings(&["self", "self_"], &[("self", "self__")]);
7499 assert_spellings(&["self", "self_", "self__"], &[("self", "self___")]);
7500 assert_spellings(&["a$b", "a_dollar_b"], &[("a$b", "a_dollar_b_")]);
7501 }
7502
7503 #[test]
7504 fn the_answer_does_not_depend_on_the_order_the_names_arrive_in() {
7505 let forwards = spellings(&["self", "self_", "crate", "crate_", "a$b", "a_dollar_b"]);
7506 let backwards = spellings(&["a_dollar_b", "a$b", "crate_", "crate", "self_", "self"]);
7507 assert_eq!(forwards, backwards);
7508 }
7509
7510 #[test]
7511 fn a_name_is_spelled_the_same_way_however_often_it_is_collected() {
7512 assert_spellings(&["self", "self", "self_", "self"], &[("self", "self__")]);
7515 }
7516}