c what is this

This commit is contained in:
nora 2023-05-23 20:42:51 +02:00
parent ffd3dad040
commit 54226ad499
11 changed files with 305 additions and 57 deletions

View file

@ -10,3 +10,4 @@ bumpalo = "3.10.0"
indexmap = "1.9.1"
parser = { path = "../parser" }
rustc-hash = "1.1.0"
smallvec = { version = "1.10.0", features = ["union"] }

View file

@ -187,6 +187,7 @@ pub enum UnaryKind {
pub enum ConstValue {
Void,
Int(u128),
StaticPtr(DefId),
}
impl Func<'_> {

View file

@ -186,6 +186,7 @@ impl Display for ConstValue {
match self {
ConstValue::Int(int) => <_ as Display>::fmt(int, f),
ConstValue::Void => f.write_str("void"),
ConstValue::StaticPtr(def_id) => write!(f, "{{{}}}", def_id.0),
}
}
}

View file

@ -1,4 +1,5 @@
mod builder;
mod typeck;
use std::cell::{Cell, RefCell};
@ -24,6 +25,7 @@ type Result<T, E = Error> = std::result::Result<T, E>;
struct LoweringCx<'cx> {
tys: RefCell<FxHashSet<&'cx TyKind<'cx>>>,
layouts: RefCell<FxHashSet<&'cx Layout>>,
string_literals: RefCell<FxHashMap<&'cx [u8], DefId>>,
arena: &'cx bumpalo::Bump,
next_def_id: Cell<DefId>,
}
@ -38,13 +40,10 @@ impl<'cx> LoweringCx<'cx> {
let kind = match ty {
ast::TypeSpecifier::Void => TyKind::Void,
ast::TypeSpecifier::Char => TyKind::Char,
ast::TypeSpecifier::SChar => TyKind::SChar,
ast::TypeSpecifier::UChar => TyKind::UChar,
ast::TypeSpecifier::Integer(int) => TyKind::Integer(*int),
ast::TypeSpecifier::Float => TyKind::Float,
ast::TypeSpecifier::Double => TyKind::Double,
ast::TypeSpecifier::LongDouble => TyKind::LongDouble,
ast::TypeSpecifier::Bool => TyKind::Bool,
};
self.intern_ty(kind)
}
@ -73,22 +72,34 @@ impl<'cx> LoweringCx<'cx> {
}
}
fn intern_str_lit(&self, str: &[u8]) -> DefId {
let opt_str = self.string_literals.borrow().get(str).copied();
match opt_str {
Some(lit_def_id) => lit_def_id,
None => {
let str = self.arena.alloc_slice_copy(str);
let lit_def_id = self.next_def_id();
self.string_literals.borrow_mut().insert(str, lit_def_id);
lit_def_id
}
}
}
fn layout_of(&self, ty: Ty<'cx>) -> TyLayout<'cx> {
let layout = match *ty {
TyKind::Void => Layout::size_align(0, 1),
TyKind::Char => Layout::size_align(1, 1),
TyKind::SChar => Layout::size_align(1, 1),
TyKind::UChar => Layout::size_align(1, 1),
TyKind::Integer(int) => match int.kind {
parser::ast::IntTyKind::Short => Layout::size_align(2, 2),
parser::ast::IntTyKind::Int => Layout::size_align(4, 4),
parser::ast::IntTyKind::Long => Layout::size_align(8, 8),
parser::ast::IntTyKind::LongLong => Layout::size_align(8, 8),
IntTyKind::Bool => Layout::size_align(1, 1),
IntTyKind::Char => Layout::size_align(1, 1),
IntTyKind::Short => Layout::size_align(2, 2),
IntTyKind::Int => Layout::size_align(4, 4),
IntTyKind::Long => Layout::size_align(8, 8),
IntTyKind::LongLong => Layout::size_align(8, 8),
},
TyKind::Float => Layout::size_align(4, 4),
TyKind::Double => Layout::size_align(8, 8),
TyKind::LongDouble => Layout::size_align(8, 8),
TyKind::Bool => Layout::size_align(1, 1),
TyKind::Struct(_) => todo!("layout_of struct"),
TyKind::Union(_) => todo!("layout_of union"),
TyKind::Enum(_) => todo!("layout_of enum"),
@ -106,6 +117,7 @@ pub fn lower_translation_unit<'cx>(
let lcx = LoweringCx {
tys: RefCell::default(),
layouts: RefCell::default(),
string_literals: RefCell::default(),
arena,
next_def_id: Cell::new(DefId(0)),
};
@ -139,11 +151,25 @@ pub fn lower_translation_unit<'cx>(
Ok(ir)
}
#[derive(Debug)]
struct FnLoweringCtxt<'a, 'cx> {
scopes: Vec<FxHashMap<Symbol, VariableInfo<'cx>>>,
build: FuncBuilder<'a, 'cx>,
lcx: &'a LoweringCx<'cx>,
types: CommonTypes<'cx>,
}
struct CommonInt<'cx> {
signed: Ty<'cx>,
unsigned: Ty<'cx>,
}
struct CommonTypes<'cx> {
void: Ty<'cx>,
char: Ty<'cx>,
su_char: CommonInt<'cx>,
short: CommonInt<'cx>,
int: CommonInt<'cx>,
long: CommonInt<'cx>,
}
impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
@ -292,18 +318,15 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
}
fn lower_expr(&mut self, expr: &ast::Expr, span: Span) -> Result<(Operand, TyLayout<'cx>)> {
match expr {
ast::Expr::Atom(ast::Atom::Char(c)) => Ok((
let op_tyl = match expr {
ast::Expr::Atom(ast::Atom::Char(c)) => (
Operand::Const(ConstValue::Int((*c).into())),
self.ty_layout(TyKind::Char),
)),
ast::Expr::Atom(ast::Atom::Int(i)) => Ok((
self.lcx.layout_of(self.types.char),
),
ast::Expr::Atom(ast::Atom::Int(i)) => (
Operand::Const(ConstValue::Int(*i as _)),
self.ty_layout(TyKind::Integer(IntTy {
sign: IntTySignedness::Signed,
kind: IntTyKind::Int,
})),
)),
self.lcx.layout_of(self.types.int.signed),
),
ast::Expr::Atom(ast::Atom::Float(_)) => todo!("no floats"),
ast::Expr::Atom(ast::Atom::Ident((ident, ident_span))) => {
let Some(var) = self.resolve_ident(*ident) else {
@ -311,9 +334,15 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
};
let tyl = var.tyl;
let op = self.build.load(var.tyl, var.ptr_to, span);
Ok((Operand::Reg(op), tyl))
(Operand::Reg(op), tyl)
}
ast::Expr::Atom(ast::Atom::String(string)) => {
let lit_def_id = self.lcx.intern_str_lit(string);
(
Operand::Const(ConstValue::StaticPtr(lit_def_id)),
self.ty_layout(TyKind::Ptr(self.types.char)),
)
}
ast::Expr::Atom(ast::Atom::String(_)) => todo!("no string literals"),
ast::Expr::Unary(ast::ExprUnary {
op: op @ (ast::UnaryOp::Increment | ast::UnaryOp::Decrement),
rhs: rhs_expr,
@ -341,7 +370,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
.binary(bin_kind, Operand::Reg(lhs), rhs, span, self.dummy_tyl());
self.build.store(lhs, rhs, self.dummy_tyl().layout, span);
Ok((Operand::Reg(result), self.dummy_tyl()))
(Operand::Reg(result), self.dummy_tyl())
}
ast::Expr::Unary(unary) => {
let rhs = self.lower_expr(&unary.rhs.0, unary.rhs.1)?;
@ -357,7 +386,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
};
let reg = self.build.unary(kind, rhs.0, span, self.dummy_tyl());
Ok((Operand::Reg(reg), self.dummy_tyl()))
(Operand::Reg(reg), self.dummy_tyl())
}
ast::Expr::Binary(ast::ExprBinary {
lhs,
@ -372,7 +401,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
let ptr_to = self.expr_as_lvalue(&lhs.0)?;
self.build
.store(ptr_to, rhs.0, self.dummy_tyl().layout, span);
Ok(rhs)
rhs
}
ast::Expr::Binary(binary) => {
let lhs = self.lower_expr(&binary.lhs.0, binary.lhs.1)?;
@ -408,7 +437,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
.build
.binary(kind, lhs.0, rhs.0, span, self.dummy_tyl());
Ok((Operand::Reg(reg), self.dummy_tyl()))
(Operand::Reg(reg), self.dummy_tyl())
}
ast::Expr::Postfix(postfix) => {
let lhs = self.lower_expr(&postfix.lhs.0, postfix.lhs.1)?;
@ -420,7 +449,7 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
.collect::<Result<_, _>>()?;
let reg = self.build.call(self.dummy_tyl(), lhs.0, args, span);
Ok((Operand::Reg(reg), self.dummy_tyl()))
(Operand::Reg(reg), self.dummy_tyl())
}
ast::PostfixOp::Member(_) => todo!("member expr"),
ast::PostfixOp::ArrowMember(_) => todo!("arrow member expr"),
@ -430,7 +459,8 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
ast::PostfixOp::Decrement => todo!(),
}
}
}
};
Ok(op_tyl)
}
}
@ -461,6 +491,7 @@ fn lower_func<'cx>(
params.len().try_into().unwrap(),
),
lcx,
types: CommonTypes::new(lcx),
};
for param in params {
@ -512,3 +543,22 @@ fn lower_func<'cx>(
Ok(cx.build.finish())
}
impl<'cx> CommonTypes<'cx> {
fn new(lcx: &LoweringCx<'cx>) -> Self {
let int = |sign, kind| lcx.intern_ty(TyKind::Integer(IntTy { sign, kind }));
let int_pair = |kind| CommonInt {
signed: int(IntTySignedness::Signed, kind),
unsigned: int(IntTySignedness::Unsigned, kind),
};
Self {
void: lcx.intern_ty(TyKind::Void),
char: lcx.intern_ty(TyKind::Char),
su_char: int_pair(IntTyKind::Char),
short: int_pair(IntTyKind::Short),
int: int_pair(IntTyKind::Int),
long: int_pair(IntTyKind::Long),
}
}
}

View file

@ -0,0 +1,160 @@
use parser::{
ast::{IntTy, IntTyKind, IntTySignedness},
Span,
};
use smallvec::{smallvec, SmallVec};
use super::{FnLoweringCtxt, Result};
use crate::{
ty::{Ty, TyKind},
Error,
};
pub(super) type Coercions<'cx> = SmallVec<[(Coercion, Ty<'cx>); 2]>;
pub(super) enum Coercion {
ZeroExt,
SignExt,
SignToUnsigned,
}
pub(super) struct ArithCoerce<'cx> {
pub result: Ty<'cx>,
pub lhs: Coercions<'cx>,
pub rhs: Coercions<'cx>,
}
impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> {
/// Performs implicit coercion.
/// §6.3.1.8 Usual arithmetic conversions
pub(super) fn arith_op(
&mut self,
lhs: Ty<'cx>,
rhs: Ty<'cx>,
span: Span,
) -> Result<(Ty<'cx>, Coercions<'cx>, Coercions<'cx>)> {
Ok(match (*lhs, *rhs) {
(TyKind::LongDouble, _) => (lhs, smallvec![], self.coerce(rhs, lhs)?),
(_, TyKind::LongDouble) => (rhs, self.coerce(lhs, rhs)?, smallvec![]),
(TyKind::Double, _) => (lhs, smallvec![], self.coerce(rhs, lhs)?),
(_, TyKind::Double) => (rhs, self.coerce(lhs, rhs)?, smallvec![]),
(TyKind::Float, _) => (lhs, smallvec![], self.coerce(rhs, lhs)?),
(_, TyKind::Float) => (rhs, self.coerce(lhs, rhs)?, smallvec![]),
(_, _) => {
let mut lhs_coerce = self.promote(lhs, span)?;
let mut rhs_coerce = self.promote(rhs, span)?;
let lhs_prom = lhs_coerce.last().map_or(lhs, |(_, ty)| *ty);
let rhs_prom = rhs_coerce.last().map_or(rhs, |(_, ty)| *ty);
let lhs_prom_int = lhs_prom.unwrap_int();
let rhs_prom_int = rhs_prom.unwrap_int();
let lhs_sign = lhs_prom_int.sign;
let rhs_sign = rhs_prom_int.sign;
let lhs_kind = lhs_prom_int.kind;
let rhs_kind = rhs_prom_int.kind;
// If both operands have the same type, then no further conversion is needed.
let result = if lhs_prom == rhs_prom {
lhs
// Otherwise, if both operands have signed integer types or both have unsigned
// integer types, the operand with the type of lesser integer conversion rank is
// converted to the type of the operand with greater rank.
} else if (lhs_sign.unsigned() && rhs_prom_int.sign.unsigned())
|| (lhs_sign.signed() && rhs_sign.signed())
{
if lhs_kind > rhs_kind {
rhs_coerce.extend(self.coerce(rhs_prom, lhs_prom)?);
lhs
} else if lhs_kind < rhs_kind {
lhs_coerce.extend(self.coerce(lhs_prom, rhs_prom)?);
rhs
} else {
unreachable!("integers must have different rank here")
}
// Otherwise, if the operand that has unsigned integer type has rank greater or
// equal to the rank of the type of the other operand, then the operand with
// signed integer type is converted to the type of the operand with unsigned
// integer type.
} else if (lhs_sign.unsigned() && lhs_kind >= rhs_kind)
|| (rhs_sign.unsigned() && rhs_kind >= lhs_kind)
{
if lhs_sign.unsigned() {
rhs_coerce.extend(self.coerce(rhs_prom, lhs_prom)?);
lhs
} else {
lhs_coerce.extend(self.coerce(lhs_prom, rhs_prom)?);
rhs
}
// Otherwise, if the type of the operand with signed integer type can represent
// all of the values of the type of the operand with unsigned integer type, then
// the operand with unsigned integer type is converted to the type of the
// operand with signed integer type.
} else if (lhs_sign.unsigned() && is_int_bigger(rhs_kind, lhs_kind))
|| (rhs_sign.unsigned() && is_int_bigger(lhs_kind, rhs_kind))
{
if lhs_sign.unsigned() {
lhs_coerce.extend(self.coerce(lhs_prom, rhs_prom)?);
rhs
} else {
rhs_coerce.extend(self.coerce(rhs_prom, lhs_prom)?);
lhs
}
// Otherwise, both operands are converted to the unsigned integer type
// corresponding to the type of the operand with signed integer type.
} else {
let kind = if lhs_sign.signed() {
lhs_kind
} else {
rhs_kind
};
let ty = self.lcx.intern_ty(TyKind::Integer(IntTy {
kind,
sign: IntTySignedness::Unsigned,
}));
lhs_coerce.extend(self.coerce(lhs_prom, ty)?);
ty
};
(result, lhs_coerce, rhs_coerce)
}
})
}
fn coerce(&mut self, from: Ty<'cx>, to: Ty<'cx>) -> Result<Coercions<'cx>> {
if from != to {
todo!("coerce.")
}
Ok(smallvec![])
}
// §6.3.1.1 Boolean, characters, and integers
fn promote(&self, ty: Ty<'cx>, span: Span) -> Result<Coercions<'cx>> {
Ok(match *ty {
TyKind::Char => smallvec![(Coercion::SignExt, self.types.int.signed)],
TyKind::Integer(int) if int.kind < IntTyKind::Int => match int.sign {
IntTySignedness::Signed => smallvec![(Coercion::SignExt, self.types.int.signed)],
IntTySignedness::Unsigned => {
smallvec![(Coercion::ZeroExt, self.types.int.unsigned)]
}
},
TyKind::Integer(_) => smallvec![],
TyKind::Enum(_) => todo!("enums are unimplemented"),
_ => return Err(Error::new(format!("cannot convert {ty} to integer"), span)),
})
}
}
fn is_int_bigger(this: IntTyKind, compared_to: IntTyKind) -> bool {
let num = |kind| match kind {
IntTyKind::Bool => 1,
IntTyKind::Char => 2,
IntTyKind::Short => 3,
IntTyKind::Int => 4,
IntTyKind::Long => 5,
IntTyKind::LongLong => 5,
};
num(this) > num(compared_to)
}

View file

@ -12,20 +12,17 @@ use parser::{
use crate::ir::DefId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, Eq)]
pub struct Ty<'cx>(&'cx TyKind<'cx>);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TyKind<'cx> {
Void,
Char,
SChar,
UChar,
Integer(IntTy),
Float,
Double,
LongDouble,
Bool,
Ptr(Ty<'cx>),
Union(UnionTy<'cx>),
Struct(StructTy<'cx>),
@ -84,14 +81,14 @@ impl Display for Ty<'_> {
match **self {
TyKind::Void => f.write_str("void"),
TyKind::Char => f.write_str("char"),
TyKind::SChar => f.write_str("signed char"),
TyKind::UChar => f.write_str("unsigned char"),
TyKind::Integer(int) => {
match int.sign {
IntTySignedness::Signed => f.write_str("signed "),
IntTySignedness::Unsigned => f.write_str("unsigned "),
}?;
match int.kind {
IntTyKind::Bool => f.write_str("_Bool"),
IntTyKind::Char => f.write_str("char"),
IntTyKind::Short => f.write_str("short"),
IntTyKind::Int => f.write_str("int"),
IntTyKind::Long => f.write_str("long"),
@ -102,7 +99,6 @@ impl Display for Ty<'_> {
TyKind::Float => f.write_str("float"),
TyKind::Double => f.write_str("double"),
TyKind::LongDouble => f.write_str("long double"),
TyKind::Bool => f.write_str("_Bool"),
TyKind::Ptr(ty) => {
write!(f, "{ty}*")
}
@ -113,11 +109,29 @@ impl Display for Ty<'_> {
}
}
impl<'cx> Ty<'cx> {
pub fn is_integral(self) -> bool {
matches!(
*self,
TyKind::Char | TyKind::SChar | TyKind::UChar | TyKind::Integer(_)
)
impl PartialEq for Ty<'_> {
fn eq(&self, other: &Self) -> bool {
// Interning.
std::ptr::eq(&self.0, &other.0)
}
}
impl Hash for Ty<'_> {
fn hash<H: Hasher>(&self, state: &mut H) {
// Interning.
std::ptr::hash(&self.0, state)
}
}
impl<'cx> Ty<'cx> {
pub fn is_integral(self) -> bool {
matches!(*self, TyKind::Char | TyKind::Integer(_))
}
pub fn unwrap_int(self) -> IntTy {
match *self {
TyKind::Integer(int) => *int,
_ => panic!("expected integer type, found {self}"),
}
}
}