From 05c617f6de823e3a8c6608ce6cc015b64044805f Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 23 May 2023 21:16:49 +0200 Subject: [PATCH] coerce --- analysis/src/ir.rs | 2 + analysis/src/ir/pretty.rs | 2 + analysis/src/lower.rs | 112 ++++++++++++++++++++++++----------- analysis/src/lower/typeck.rs | 55 +++++++++++++---- analysis/src/ty.rs | 27 ++++----- parser/src/ast.rs | 8 +-- parser/src/parser.rs | 12 ++-- parser/src/pretty.rs | 4 +- 8 files changed, 150 insertions(+), 72 deletions(-) diff --git a/analysis/src/ir.rs b/analysis/src/ir.rs index 97ee1cc..b0ac1ee 100644 --- a/analysis/src/ir.rs +++ b/analysis/src/ir.rs @@ -178,6 +178,8 @@ pub enum BinKind { #[derive(Debug, Clone, Copy)] pub enum UnaryKind { + Zext, + Sext, Negate, BitNot, LogicalNot, diff --git a/analysis/src/ir/pretty.rs b/analysis/src/ir/pretty.rs index 1c62e7f..627fefc 100644 --- a/analysis/src/ir/pretty.rs +++ b/analysis/src/ir/pretty.rs @@ -130,6 +130,8 @@ impl PrettyPrinter { " {} = {} {}", print_reg(result), match kind { + UnaryKind::Zext => "zext", + UnaryKind::Sext => "sext", UnaryKind::Negate => "negate", UnaryKind::BitNot => "bitnot", UnaryKind::LogicalNot => "logicalnot", diff --git a/analysis/src/lower.rs b/analysis/src/lower.rs index c7ceb0d..a8b0199 100644 --- a/analysis/src/lower.rs +++ b/analysis/src/lower.rs @@ -4,12 +4,12 @@ mod typeck; use std::cell::{Cell, RefCell}; use parser::{ - ast::{self, IntTy, IntTyKind, IntTySignedness}, + ast::{self, ExprBinary, IntTy, IntTyKind, IntSign}, Span, Symbol, }; use rustc_hash::{FxHashMap, FxHashSet}; -use self::builder::FuncBuilder; +use self::{builder::FuncBuilder, typeck::Coercion}; use crate::{ ir::{ self, BbIdx, BinKind, Branch, ConstValue, DefId, Func, Ir, Layout, Operand, Register, @@ -40,7 +40,7 @@ impl<'cx> LoweringCx<'cx> { let kind = match ty { ast::TypeSpecifier::Void => TyKind::Void, ast::TypeSpecifier::Char => TyKind::Char, - ast::TypeSpecifier::Integer(int) => TyKind::Integer(*int), + ast::TypeSpecifier::Integer(int) => TyKind::Int(*int), ast::TypeSpecifier::Float => TyKind::Float, ast::TypeSpecifier::Double => TyKind::Double, ast::TypeSpecifier::LongDouble => TyKind::LongDouble, @@ -89,7 +89,7 @@ impl<'cx> LoweringCx<'cx> { let layout = match *ty { TyKind::Void => Layout::size_align(0, 1), TyKind::Char => Layout::size_align(1, 1), - TyKind::Integer(int) => match int.kind { + TyKind::Int(int) => match int.kind { IntTyKind::Bool => Layout::size_align(1, 1), IntTyKind::Char => Layout::size_align(1, 1), IntTyKind::Short => Layout::size_align(2, 2), @@ -403,34 +403,66 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { .store(ptr_to, rhs.0, self.dummy_tyl().layout, span); rhs } - ast::Expr::Binary(binary) => { - let lhs = self.lower_expr(&binary.lhs.0, binary.lhs.1)?; - let rhs = self.lower_expr(&binary.rhs.0, binary.rhs.1)?; - let kind = match binary.op { - ast::BinaryOp::Arith(ast::ArithOpKind::Mul) => BinKind::Mul, - ast::BinaryOp::Arith(ast::ArithOpKind::Div) => BinKind::Div, - ast::BinaryOp::Arith(ast::ArithOpKind::Mod) => BinKind::Mod, - ast::BinaryOp::Arith(ast::ArithOpKind::Add) => BinKind::Add, - ast::BinaryOp::Arith(ast::ArithOpKind::Sub) => BinKind::Sub, - ast::BinaryOp::Arith(ast::ArithOpKind::Shl) => BinKind::Shl, - ast::BinaryOp::Arith(ast::ArithOpKind::Shr) => BinKind::Shr, - ast::BinaryOp::Arith(ast::ArithOpKind::BitAnd) => BinKind::BitAnd, - ast::BinaryOp::Arith(ast::ArithOpKind::BitXor) => BinKind::BitXor, - ast::BinaryOp::Arith(ast::ArithOpKind::BitOr) => BinKind::BitOr, - ast::BinaryOp::LogicalAnd => todo!("no logical or"), - ast::BinaryOp::LogicalOr => todo!("no logical and"), - ast::BinaryOp::Comparison(ast::ComparisonKind::Lt) => BinKind::Lt, - ast::BinaryOp::Comparison(ast::ComparisonKind::Gt) => BinKind::Gt, - ast::BinaryOp::Comparison(ast::ComparisonKind::LtEq) => BinKind::Leq, - ast::BinaryOp::Comparison(ast::ComparisonKind::GtEq) => BinKind::Geq, - ast::BinaryOp::Comparison(ast::ComparisonKind::Eq) => BinKind::Eq, - ast::BinaryOp::Comparison(ast::ComparisonKind::Neq) => BinKind::Neq, - ast::BinaryOp::Comma => { - // Discard the lhs, evaluate to the rhs. - return Ok(rhs); - } - ast::BinaryOp::Index => todo!("no index"), - ast::BinaryOp::Assign(_) => unreachable!("assign handled above"), + ast::Expr::Binary(ExprBinary { + op: ast::BinaryOp::Arith(arith), + lhs: lhs_expr, + rhs: rhs_expr, + }) => { + let (mut lhs, lhs_tyl) = self.lower_expr(&lhs_expr.0, lhs_expr.1)?; + let (mut rhs, rhs_tyl) = self.lower_expr(&rhs_expr.0, rhs_expr.1)?; + + let (result, lhs_coerce, rhs_coerce) = + self.arith_op(lhs_tyl.ty, rhs_tyl.ty, span)?; + + let mut do_coerce = |reg, coerce, span, ty| { + let kind = match coerce { + Coercion::ZeroExt => UnaryKind::Zext, + Coercion::SignExt => UnaryKind::Sext, + Coercion::SignToUnsigned => todo!("hm, what should this do"), + }; + Operand::Reg(self.build.unary(kind, reg, span, self.lcx.layout_of(ty))) + }; + + for (coerce, ty) in lhs_coerce { + lhs = do_coerce(lhs, coerce, span, ty); + } + for (coerce, ty) in rhs_coerce { + rhs = do_coerce(rhs, coerce, span, ty); + } + + let kind = match arith { + ast::ArithOpKind::Mul => BinKind::Mul, + ast::ArithOpKind::Div => BinKind::Div, + ast::ArithOpKind::Mod => BinKind::Mod, + ast::ArithOpKind::Add => BinKind::Add, + ast::ArithOpKind::Sub => BinKind::Sub, + ast::ArithOpKind::Shl => BinKind::Shl, + ast::ArithOpKind::Shr => BinKind::Shr, + ast::ArithOpKind::BitAnd => BinKind::BitAnd, + ast::ArithOpKind::BitXor => BinKind::BitXor, + ast::ArithOpKind::BitOr => BinKind::BitOr, + }; + + let reg = self + .build + .binary(kind, lhs, rhs, span, self.lcx.layout_of(result)); + + (Operand::Reg(reg), self.dummy_tyl()) + } + ast::Expr::Binary(ExprBinary { + op: ast::BinaryOp::Comparison(comp), + lhs, + rhs, + }) => { + let lhs = self.lower_expr(&lhs.0, lhs.1)?; + let rhs = self.lower_expr(&rhs.0, rhs.1)?; + let kind = match comp { + ast::ComparisonKind::Lt => BinKind::Lt, + ast::ComparisonKind::Gt => BinKind::Gt, + ast::ComparisonKind::LtEq => BinKind::Leq, + ast::ComparisonKind::GtEq => BinKind::Geq, + ast::ComparisonKind::Eq => BinKind::Eq, + ast::ComparisonKind::Neq => BinKind::Neq, }; let reg = self @@ -439,6 +471,16 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { (Operand::Reg(reg), self.dummy_tyl()) } + ast::Expr::Binary(ExprBinary { + op: ast::BinaryOp::Comma, + lhs, + rhs, + }) => { + let _lhs = self.lower_expr(&lhs.0, lhs.1)?; + // Discard the lhs, evaluate to the rhs. + self.lower_expr(&rhs.0, rhs.1)? + } + ast::Expr::Binary(_) => todo!("other binary"), ast::Expr::Postfix(postfix) => { let lhs = self.lower_expr(&postfix.lhs.0, postfix.lhs.1)?; match &postfix.op { @@ -546,10 +588,10 @@ fn lower_func<'cx>( impl<'cx> CommonTypes<'cx> { fn new(lcx: &LoweringCx<'cx>) -> Self { - let int = |sign, kind| lcx.intern_ty(TyKind::Integer(IntTy { sign, kind })); + let int = |sign, kind| lcx.intern_ty(TyKind::Int(IntTy { sign, kind })); let int_pair = |kind| CommonInt { - signed: int(IntTySignedness::Signed, kind), - unsigned: int(IntTySignedness::Unsigned, kind), + signed: int(IntSign::Signed, kind), + unsigned: int(IntSign::Unsigned, kind), }; Self { diff --git a/analysis/src/lower/typeck.rs b/analysis/src/lower/typeck.rs index c73dc5b..7e96a80 100644 --- a/analysis/src/lower/typeck.rs +++ b/analysis/src/lower/typeck.rs @@ -1,5 +1,5 @@ use parser::{ - ast::{IntTy, IntTyKind, IntTySignedness}, + ast::{IntSign, IntTy, IntTyKind}, Span, }; use smallvec::{smallvec, SmallVec}; @@ -109,9 +109,9 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { } else { rhs_kind }; - let ty = self.lcx.intern_ty(TyKind::Integer(IntTy { + let ty = self.lcx.intern_ty(TyKind::Int(IntTy { kind, - sign: IntTySignedness::Unsigned, + sign: IntSign::Unsigned, })); lhs_coerce.extend(self.coerce(lhs_prom, ty)?); ty @@ -123,23 +123,58 @@ impl<'a, 'cx> FnLoweringCtxt<'a, 'cx> { } fn coerce(&mut self, from: Ty<'cx>, to: Ty<'cx>) -> Result> { - if from != to { - todo!("coerce.") + if from == to { + return Ok(smallvec![]); } - Ok(smallvec![]) + Ok(match (*from, *to) { + ( + TyKind::Char, + TyKind::Int(IntTy { + sign: IntSign::Signed, + .. + }), + ) => { + smallvec![(Coercion::SignExt, to)] + } + ( + TyKind::Int(IntTy { + sign: IntSign::Signed, + kind: from_kind, + }), + TyKind::Int(IntTy { + sign: IntSign::Signed, + kind: to_kind, + }), + ) if from_kind < to_kind => { + smallvec![(Coercion::SignExt, to)] + } + ( + TyKind::Int(IntTy { + sign: IntSign::Unsigned, + kind: from_kind, + }), + TyKind::Int(IntTy { + sign: IntSign::Unsigned, + kind: to_kind, + }), + ) if from_kind < to_kind => { + smallvec![(Coercion::ZeroExt, to)] + } + _ => panic!("i cant coerce that"), + }) } // ยง6.3.1.1 Boolean, characters, and integers fn promote(&self, ty: Ty<'cx>, span: Span) -> Result> { 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 => { + TyKind::Int(int) if int.kind < IntTyKind::Int => match int.sign { + IntSign::Signed => smallvec![(Coercion::SignExt, self.types.int.signed)], + IntSign::Unsigned => { smallvec![(Coercion::ZeroExt, self.types.int.unsigned)] } }, - TyKind::Integer(_) => smallvec![], + TyKind::Int(_) => smallvec![], TyKind::Enum(_) => todo!("enums are unimplemented"), _ => return Err(Error::new(format!("cannot convert {ty} to integer"), span)), }) diff --git a/analysis/src/ty.rs b/analysis/src/ty.rs index d3f2fa6..73a7618 100644 --- a/analysis/src/ty.rs +++ b/analysis/src/ty.rs @@ -6,7 +6,7 @@ use std::{ use indexmap::IndexMap; use parser::{ - ast::{IntTy, IntTyKind, IntTySignedness}, + ast::{IntTy, IntTyKind, IntSign}, Symbol, }; @@ -19,7 +19,7 @@ pub struct Ty<'cx>(&'cx TyKind<'cx>); pub enum TyKind<'cx> { Void, Char, - Integer(IntTy), + Int(IntTy), Float, Double, LongDouble, @@ -47,12 +47,6 @@ pub struct EnumTy { pub variants: IndexMap, } -impl<'cx> Ty<'cx> { - pub fn new_unchecked(kind: &'cx TyKind<'cx>) -> Self { - Self(kind) - } -} - impl<'cx> Deref for Ty<'cx> { type Target = &'cx TyKind<'cx>; fn deref(&self) -> &Self::Target { @@ -81,10 +75,10 @@ impl Display for Ty<'_> { match **self { TyKind::Void => f.write_str("void"), TyKind::Char => f.write_str("char"), - TyKind::Integer(int) => { + TyKind::Int(int) => { match int.sign { - IntTySignedness::Signed => f.write_str("signed "), - IntTySignedness::Unsigned => f.write_str("unsigned "), + IntSign::Signed => f.write_str("signed "), + IntSign::Unsigned => f.write_str("unsigned "), }?; match int.kind { IntTyKind::Bool => f.write_str("_Bool"), @@ -112,25 +106,28 @@ impl Display for Ty<'_> { impl PartialEq for Ty<'_> { fn eq(&self, other: &Self) -> bool { // Interning. - std::ptr::eq(&self.0, &other.0) + std::ptr::eq(self.0, other.0) } } impl Hash for Ty<'_> { fn hash(&self, state: &mut H) { // Interning. - std::ptr::hash(&self.0, state) + std::ptr::hash(self.0, state) } } impl<'cx> Ty<'cx> { + pub fn new_unchecked(kind: &'cx TyKind<'cx>) -> Self { + Self(kind) + } pub fn is_integral(self) -> bool { - matches!(*self, TyKind::Char | TyKind::Integer(_)) + matches!(*self, TyKind::Char | TyKind::Int(_)) } pub fn unwrap_int(self) -> IntTy { match *self { - TyKind::Integer(int) => *int, + TyKind::Int(int) => *int, _ => panic!("expected integer type, found {self}"), } } diff --git a/parser/src/ast.rs b/parser/src/ast.rs index d805b18..e344f79 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -145,12 +145,12 @@ pub enum Stmt { // #[derive(Debug, DebugPls, Clone, Copy, PartialEq, Eq, Hash)] -pub enum IntTySignedness { +pub enum IntSign { Signed, Unsigned, } -impl Default for IntTySignedness { +impl Default for IntSign { fn default() -> Self { // C defaults to unsigned for integers. Self::Signed @@ -170,7 +170,7 @@ pub enum IntTyKind { #[derive(Debug, DebugPls, Clone, Copy, PartialEq, Eq, Hash)] pub struct IntTy { - pub sign: IntTySignedness, + pub sign: IntSign, pub kind: IntTyKind, } @@ -295,7 +295,7 @@ impl DirectDeclarator { } } -impl IntTySignedness { +impl IntSign { pub fn signed(self) -> bool { matches!(self, Self::Signed) } diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 6e3cf58..37a8ab2 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -4,7 +4,7 @@ use peekmore::PeekMoreIterator; use crate::{ ast::{ Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, ExternalDecl, FunctionDef, - FunctionParamDecl, Ident, InitDecl, IntTy, IntTyKind, IntTySignedness, NormalDecl, Stmt, + FunctionParamDecl, Ident, InitDecl, IntTy, IntTyKind, IntSign, NormalDecl, Stmt, TranslationUnit, TypeSpecifier, }, pre::Punctuator as P, @@ -335,11 +335,11 @@ where self.peek_t() { // the signed is an integer qualifier - signedness = Some(IntTySignedness::Signed); + signedness = Some(IntSign::Signed); continue; } TypeSpecifier::Integer(IntTy { - sign: IntTySignedness::Signed, + sign: IntSign::Signed, kind: IntTyKind::Int, }) } @@ -354,18 +354,18 @@ where self.peek_t() { // the unsigned is an integer qualifier - signedness = Some(IntTySignedness::Unsigned); + signedness = Some(IntSign::Unsigned); continue; } TypeSpecifier::Integer(IntTy { - sign: IntTySignedness::Unsigned, + sign: IntSign::Unsigned, kind: IntTyKind::Int, }) } Tok::Kw(Kw::Float) => TypeSpecifier::Float, Tok::Kw(Kw::Double) => TypeSpecifier::Double, Tok::Kw(Kw::Bool) => TypeSpecifier::Integer(IntTy { - sign: IntTySignedness::Unsigned, + sign: IntSign::Unsigned, kind: IntTyKind::Bool, }), Tok::Kw(Kw::Complex) => { diff --git a/parser/src/pretty.rs b/parser/src/pretty.rs index dce7af8..045382b 100644 --- a/parser/src/pretty.rs +++ b/parser/src/pretty.rs @@ -6,7 +6,7 @@ use crate::{ ast::{ ArithOpKind, Atom, BinaryOp, ComparisonKind, Decl, DeclAttr, DeclSpec, Declarator, DirectDeclarator, Expr, ExprBinary, ExprPostfix, ExprUnary, ExternalDecl, FunctionDef, - FunctionParamDecl, InitDecl, IntTyKind, IntTySignedness, NormalDecl, PostfixOp, Stmt, + FunctionParamDecl, InitDecl, IntTyKind, IntSign, NormalDecl, PostfixOp, Stmt, TypeSpecifier, UnaryOp, }, sym::Symbol, @@ -233,7 +233,7 @@ impl PrettyPrinter { TypeSpecifier::Char => self.string("char"), TypeSpecifier::Integer(int) => { // prefix the unsignedness if desired - if let IntTySignedness::Unsigned = int.sign { + if let IntSign::Unsigned = int.sign { self.string("unsigned ")?; }