use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use rucc_base::{Interner, Symbol};
use rucc_diag::{Diagnostic, Span};
use rucc_ir::{
DataList, Datum, Func, Global, Imm, Linkage as IrLinkage, Module, Reloc, Signature, TlsModel,
};
use rucc_sema::{
Base, Const, DeclId, DeclKind, Definition, Eval, ExprId, ExprKind, InitEntry, InitList,
Linkage, StorageDuration, StrId, Tast,
};
use rucc_target::TargetInfo;
use rucc_types::{TypeId, TypeKind, Types};
use crate::body;
use crate::repr;
#[derive(Debug)]
pub struct Context<'a> {
pub tast: &'a Tast,
pub types: &'a Types,
pub target: &'a TargetInfo,
pub names: &'a mut Interner,
}
#[derive(Debug)]
pub struct Lowered {
pub module: Module,
pub diagnostics: Vec<Diagnostic>,
}
#[must_use]
pub fn lower(name: &str, cx: Context<'_>) -> Lowered {
let Context { tast, types, target, names } = cx;
let module = Module::new(names.intern(name), target);
let mut unit = Unit {
tast,
types,
target,
names,
module,
diagnostics: Vec::new(),
strings: HashMap::new(),
statics: HashMap::new(),
done: HashSet::new(),
};
unit.run();
Lowered { module: unit.module, diagnostics: unit.diagnostics }
}
pub(crate) struct Unit<'a> {
pub(crate) tast: &'a Tast,
pub(crate) types: &'a Types,
pub(crate) target: &'a TargetInfo,
pub(crate) names: &'a mut Interner,
pub(crate) module: Module,
pub(crate) diagnostics: Vec<Diagnostic>,
strings: HashMap<StrId, Symbol>,
statics: HashMap<DeclId, Symbol>,
done: HashSet<DeclId>,
}
impl std::fmt::Debug for Unit<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Unit")
.field("module", &self.module.counts())
.field("diagnostics", &self.diagnostics.len())
.finish()
}
}
impl Unit<'_> {
fn run(&mut self) {
for index in 0..self.tast.top_level().len() {
let decl = self.tast.top_level()[index];
if !self.done.insert(decl) {
continue;
}
match self.tast[decl].kind {
DeclKind::Function => self.function(decl),
DeclKind::Object => self.object(decl),
}
}
}
fn object(&mut self, decl: DeclId) {
let tast = self.tast;
let node = &tast[decl];
let (ty, state, init) = (node.ty, node.state, node.init);
let (linkage, duration, alignment) = (node.linkage, node.duration, node.alignment);
let span = tast.decl_span(decl);
if duration == StorageDuration::Automatic {
return;
}
let symbol = self.symbol_of(decl);
let size = repr::size_of(self.types, self.target, ty);
let align = alignment.unwrap_or_else(|| repr::align_of(self.types, self.target, ty));
let mut global = Global::new(symbol, size, align);
global.linkage = match linkage {
Linkage::External => IrLinkage::External,
Linkage::Internal | Linkage::None => IrLinkage::Internal,
};
global.tls = (duration == StorageDuration::Thread).then_some(TlsModel::GlobalDynamic);
global.constant = repr::is_read_only(self.types, ty);
global.init = match state {
Definition::Declared => None,
Definition::Tentative => Some(self.zeros(size)),
Definition::Defined => Some(self.image(init, size, span)),
};
self.module.add_global(global);
}
fn function(&mut self, decl: DeclId) {
let tast = self.tast;
let node = &tast[decl];
let (ty, linkage, body) = (node.ty, node.linkage, node.body);
let span = tast.decl_span(decl);
let Some(name) = node.name else { return };
let Some(signature) = self.signature(ty, span) else { return };
let mut func = Func::new(name, signature);
func.linkage = match linkage {
Linkage::Internal | Linkage::None => IrLinkage::Internal,
Linkage::External => IrLinkage::External,
};
if body.is_some() {
body::lower(self, decl, &mut func);
}
self.module.add_func(func);
}
pub(crate) fn signature(&mut self, ty: TypeId, span: Span) -> Option<Signature> {
let canonical = self.types.canonical(ty);
let canonical = match self.types.kind(canonical) {
TypeKind::Pointer(pointee) => self.types.canonical(pointee),
_ => canonical,
};
let TypeKind::Function(id) = self.types.kind(canonical) else {
self.unsupported("a call through something that is not a function", span);
return None;
};
let signature = self.types.signature(id);
let (ret, variadic) = (signature.ret, signature.variadic);
let prototyped = signature.prototyped;
let params = signature.params.clone();
let mut lowered = Signature::new();
lowered.variadic = variadic || !prototyped;
for param in params {
match repr::value_type(self.types, self.target, param) {
Some(ty) => lowered.params.push(ty),
None => {
self.unsupported("passing a structure or a union by value", span);
return None;
}
}
}
if !matches!(self.types.kind(self.types.canonical(ret)), TypeKind::Void) {
match repr::value_type(self.types, self.target, ret) {
Some(ty) => lowered.returns.push(ty),
None => {
self.unsupported("returning a structure or a union by value", span);
return None;
}
}
}
Some(lowered)
}
pub(crate) fn image(&mut self, init: Option<InitList>, size: u64, span: Span) -> DataList {
let Some(init) = init else { return self.zeros(size) };
let entries: Vec<InitEntry> = self.tast[init].to_vec();
let mut data: Vec<Datum> = Vec::with_capacity(entries.len());
let mut at = 0;
for entry in entries {
if entry.bit_width != 0 {
self.unsupported("a bit-field with a static storage duration", span);
continue;
}
let room = size.saturating_sub(entry.offset);
let Some(datum) = self.datum(entry.value, room) else { continue };
match entry.offset.cmp(&at) {
Ordering::Greater => data.push(Datum::Zero(entry.offset - at)),
Ordering::Less => {
self.unsupported("an initializer that writes over an earlier one", span);
continue;
}
Ordering::Equal => {}
}
at = entry.offset + datum.size(&self.module);
data.push(datum);
}
if at < size {
data.push(Datum::Zero(size - at));
}
self.module.push_data(&data)
}
fn datum(&mut self, value: ExprId, room: u64) -> Option<Datum> {
let tast = self.tast;
let ty = tast[value].ty;
let span = tast.expr_span(value);
if let TypeKind::Array { .. } = self.types.kind(self.types.canonical(ty)) {
let ExprKind::Str(id) = tast[value].kind else {
self.unsupported("this initializer", span);
return None;
};
let bytes = tast[id].bytes(self.target);
let take = bytes.len().min(usize::try_from(room).unwrap_or(usize::MAX));
return Some(Datum::Bytes(self.module.push_bytes(&bytes[..take])));
}
let size = repr::size_of(self.types, self.target, ty);
match self.fold(value)? {
Const::Int(number) => {
let ty = repr::value_type(self.types, self.target, ty)?;
let imm = self.module.add_imm(Imm::int(number, ty));
Some(Datum::Scalar { ty, value: imm })
}
Const::Float(number) => {
let ty = repr::value_type(self.types, self.target, ty)?;
let imm = self.module.add_imm(Imm::from_bits(number.to_bits()));
Some(Datum::Scalar { ty, value: imm })
}
Const::Address(address) => {
let symbol = match address.base {
Base::Decl(decl) => self.symbol_of(decl),
Base::Str(id) => self.string(id),
};
let addend = i64::try_from(address.offset).unwrap_or(0);
let size = u32::try_from(size).unwrap_or(0);
Some(Datum::Addr(self.module.add_reloc(Reloc { symbol, addend, size })))
}
}
}
fn zeros(&mut self, size: u64) -> DataList {
if size == 0 {
return DataList::EMPTY;
}
self.module.push_data(&[Datum::Zero(size)])
}
pub(crate) fn string(&mut self, id: StrId) -> Symbol {
if let Some(&symbol) = self.strings.get(&id) {
return symbol;
}
let literal = &self.tast[id];
let bytes = literal.bytes(self.target);
let align = literal.encoding.element_width(self.target) / 8;
let symbol = self.names.intern(&format!(".Lstr.{}", self.strings.len()));
let mut global = Global::new(symbol, bytes.len() as u64, align.max(1));
global.linkage = IrLinkage::Internal;
global.constant = true;
let range = self.module.push_bytes(&bytes);
global.init = Some(self.module.push_data(&[Datum::Bytes(range)]));
self.module.add_global(global);
self.strings.insert(id, symbol);
symbol
}
pub(crate) fn symbol_of(&mut self, decl: DeclId) -> Symbol {
let tast = self.tast;
let node = &tast[decl];
if node.linkage != Linkage::None {
return node.name.unwrap_or_else(|| self.names.intern(".Lanon"));
}
if let Some(&symbol) = self.statics.get(&decl) {
return symbol;
}
let base = match node.name {
Some(name) => self.names.resolve(name).to_string(),
None => ".Lanon".to_string(),
};
let symbol = self.names.intern(&format!("{base}.{}", self.statics.len()));
self.statics.insert(decl, symbol);
symbol
}
pub(crate) fn local_static(&mut self, decl: DeclId) {
if !self.done.insert(decl) {
return;
}
match self.tast[decl].kind {
DeclKind::Function => self.function(decl),
DeclKind::Object => self.object(decl),
}
}
fn fold(&mut self, expr: ExprId) -> Option<Const> {
let mut eval = Eval::new(self.tast, self.types, self.target, self.names);
let folded = eval.constant(expr);
let reported = eval.finish();
self.diagnostics.extend(reported);
match folded {
Ok(value) => Some(value),
Err(stop) => {
if !stop.poisoned {
let span = self.tast.expr_span(stop.at);
self.unsupported("an initializer this compiler cannot fold", span);
}
None
}
}
}
pub(crate) fn unsupported(&mut self, what: &str, span: Span) {
self.diagnostics.push(
Diagnostic::error(format!("{what} is not supported yet"), span).with_code("E0519"),
);
}
}