Compare commits

...

5 Commits

Author SHA1 Message Date
c03a5188ea Finish casting codegen 2025-07-22 14:12:31 +03:00
e73b939de0 Add all cast instructions to lib 2025-07-22 14:05:50 +03:00
a6c9752883 Use unordered comparisons in floats 2025-07-22 13:26:08 +03:00
104205ee5d Type-infer and check typecasting 2025-07-22 13:24:53 +03:00
3378f556ec Add AST -> MIR for typecasting 2025-07-21 21:28:39 +03:00
15 changed files with 587 additions and 249 deletions

View File

@ -18,9 +18,9 @@ fn main() {
FunctionFlags::default(),
);
let arg = m_entry.build("const", Constant(I32(5))).unwrap();
let arg = m_entry.build_named("const", Constant(I32(5))).unwrap();
let fibonacci_call = m_entry
.build("const", FunctionCall(fibonacci.value(), vec![arg]))
.build_named("const", FunctionCall(fibonacci.value(), vec![arg]))
.unwrap();
m_entry
.terminate(TerminatorKind::Ret(fibonacci_call))
@ -28,10 +28,10 @@ fn main() {
let mut f_entry = fibonacci.block("entry");
let num_3 = f_entry.build("const", Constant(I32(3))).unwrap();
let param_n = f_entry.build("param", Param(0)).unwrap();
let num_3 = f_entry.build_named("const", Constant(I32(3))).unwrap();
let param_n = f_entry.build_named("param", Param(0)).unwrap();
let cond = f_entry
.build("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
.build_named("cmp", ICmp(CmpPredicate::LT, param_n, num_3))
.unwrap();
let mut then_b = fibonacci.block("then");
@ -41,21 +41,21 @@ fn main() {
.terminate(TerminatorKind::CondBr(cond, then_b.value(), else_b.value()))
.unwrap();
let ret_const = then_b.build("const", Constant(I32(1))).unwrap();
let ret_const = then_b.build_named("const", Constant(I32(1))).unwrap();
then_b.terminate(TerminatorKind::Ret(ret_const)).unwrap();
let const_1 = else_b.build("const", Constant(I32(1))).unwrap();
let const_2 = else_b.build("const", Constant(I32(2))).unwrap();
let param_1 = else_b.build("sub", Sub(param_n, const_1)).unwrap();
let param_2 = else_b.build("sub", Sub(param_n, const_2)).unwrap();
let const_1 = else_b.build_named("const", Constant(I32(1))).unwrap();
let const_2 = else_b.build_named("const", Constant(I32(2))).unwrap();
let param_1 = else_b.build_named("sub", Sub(param_n, const_1)).unwrap();
let param_2 = else_b.build_named("sub", Sub(param_n, const_2)).unwrap();
let call_1 = else_b
.build("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_1]))
.unwrap();
let call_2 = else_b
.build("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
.build_named("fibonacci", FunctionCall(fibonacci.value(), vec![param_2]))
.unwrap();
let add = else_b.build("add", Add(call_1, call_2)).unwrap();
let add = else_b.build_named("add", Add(call_1, call_2)).unwrap();
else_b.terminate(TerminatorKind::Ret(add)).unwrap();

View File

@ -4,8 +4,8 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData, NamedStruct,
TerminatorKind, Type, TypeCategory, TypeData,
Block, BlockData, CustomTypeKind, FunctionData, Instr, InstructionData, ModuleData,
NamedStruct, TerminatorKind, Type, TypeCategory, TypeData,
debug_information::{
DebugInformation, DebugLocationValue, DebugMetadataValue, DebugProgramValue,
InstructionDebugRecordData,
@ -467,6 +467,18 @@ impl Builder {
_ => Err(()),
}
}
Instr::Trunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::ZExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPTrunc(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPExt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToUI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::FPToSI(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::UIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::SIToFP(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::PtrToInt(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::IntToPtr(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
Instr::BitCast(instr, ty) => instr.cast_to(self, &ty).map(|_| ()),
}
}
}
@ -505,3 +517,125 @@ impl Builder {
}
}
}
impl InstructionValue {
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
unsafe {
block.builder.add_instruction_location(&self, location);
}
self
}
pub fn maybe_location(
self,
block: &mut Block,
location: Option<DebugLocationValue>,
) -> InstructionValue {
unsafe {
if let Some(location) = location {
block.builder.add_instruction_location(&self, location);
}
}
self
}
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
unsafe {
block.builder.add_instruction_record(self, record);
}
}
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(()),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
GetElemPtr(instr, _) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = &instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
match *inner_ty.clone() {
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
_ => Ok(instr_ty),
}
}
GetStructElemPtr(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
let Type::CustomType(ty_value) = *inner_ty else {
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
};
let field_ty = match builder.type_data(&ty_value).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
fields.get_unchecked(*idx as usize).clone()
}
};
Ok(Type::Ptr(Box::new(field_ty)))
}
ExtractValue(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
Ok(match instr_ty {
Type::CustomType(struct_ty) => {
let data = builder.type_data(&struct_ty);
match data.kind {
CustomTypeKind::NamedStruct(named_struct) => {
named_struct.1.get(*idx as usize).unwrap().clone()
}
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(()),
})
}
Trunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
ZExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPTrunc(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPExt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToUI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
FPToSI(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
UIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
SIToFP(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
PtrToInt(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
IntToPtr(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
BitCast(instr, ty) => instr.cast_to(builder, ty).map(|_| ty.clone()),
}
}
}
fn cast_to(&self, builder: &Builder, ty: &Type) -> Result<Instr, ()> {
self.get_type(builder)?
.cast_instruction(*self, &ty)
.ok_or(())
}
}

View File

@ -943,6 +943,78 @@ impl InstructionHolder {
*idx,
name.as_ptr(),
),
Trunc(instr_val, ty) => LLVMBuildTrunc(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
ZExt(instr_val, ty) => LLVMBuildZExt(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
SExt(instr_val, ty) => LLVMBuildSExt(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
FPTrunc(instr_val, ty) => LLVMBuildTrunc(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
FPExt(instr_val, ty) => LLVMBuildFPExt(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
FPToUI(instr_val, ty) => LLVMBuildFPToUI(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
FPToSI(instr_val, ty) => LLVMBuildFPToSI(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
UIToFP(instr_val, ty) => LLVMBuildUIToFP(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
SIToFP(instr_val, ty) => LLVMBuildSIToFP(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
PtrToInt(instr_val, ty) => LLVMBuildPtrToInt(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
IntToPtr(instr_val, ty) => LLVMBuildIntToPtr(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
BitCast(instr_val, ty) => LLVMBuildBitCast(
module.builder_ref,
module.values.get(instr_val).unwrap().value_ref,
ty.as_llvm(module.context_ref, &module.types),
name.as_ptr(),
),
}
};
if let Some(record) = &self.record {
@ -1070,12 +1142,12 @@ impl CmpPredicate {
use CmpPredicate::*;
use LLVMRealPredicate::*;
match self {
LT => LLVMRealOLT,
LE => LLVMRealOLE,
GT => LLVMRealOGT,
GE => LLVMRealOGE,
EQ => LLVMRealOEQ,
NE => LLVMRealONE,
LT => LLVMRealULT,
LE => LLVMRealULE,
GT => LLVMRealUGT,
GE => LLVMRealUGE,
EQ => LLVMRealUEQ,
NE => LLVMRealUNE,
}
}
}

View File

@ -342,6 +342,42 @@ impl Debug for Instr {
Instr::ExtractValue(instruction_value, index) => {
fmt_index(f, instruction_value, &index.to_string())
}
Instr::Trunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::ZExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPTrunc(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPExt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToUI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::FPToSI(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::UIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::SIToFP(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::PtrToInt(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::IntToPtr(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
Instr::BitCast(instr_val, ty) => {
write!(f, "{:?} to {:?} ({})", instr_val, ty, self.default_name())
}
}
}
}

View File

@ -233,12 +233,24 @@ impl Instr {
Instr::ICmp(..) => "icmp",
Instr::FunctionCall(..) => "call",
Instr::FCmp(_, _, _) => "fcmp",
Instr::Trunc(_, _) => "trunc",
Instr::ZExt(_, _) => "zext",
Instr::SExt(_, _) => "sext",
Instr::FPTrunc(_, _) => "fptrunc",
Instr::FPExt(_, _) => "pfext",
Instr::FPToUI(_, _) => "fptoui",
Instr::FPToSI(_, _) => "fptosi",
Instr::UIToFP(_, _) => "uitofp",
Instr::SIToFP(_, _) => "sitofp",
Instr::PtrToInt(_, _) => "ptrtoint",
Instr::IntToPtr(_, _) => "inttoptr",
Instr::BitCast(_, _) => "bitcast",
}
}
}
impl<'builder> Block<'builder> {
pub fn build<T: Into<String>>(
pub fn build_named<T: Into<String>>(
&mut self,
name: T,
instruction: Instr,
@ -256,7 +268,7 @@ impl<'builder> Block<'builder> {
}
}
pub fn build_anon(&mut self, instruction: Instr) -> Result<InstructionValue, ()> {
pub fn build(&mut self, instruction: Instr) -> Result<InstructionValue, ()> {
unsafe {
let name = instruction.default_name().to_owned();
self.builder.add_instruction(
@ -310,34 +322,6 @@ impl<'builder> Block<'builder> {
}
}
impl InstructionValue {
pub fn with_location(self, block: &Block, location: DebugLocationValue) -> InstructionValue {
unsafe {
block.builder.add_instruction_location(&self, location);
}
self
}
pub fn maybe_location(
self,
block: &mut Block,
location: Option<DebugLocationValue>,
) -> InstructionValue {
unsafe {
if let Some(location) = location {
block.builder.add_instruction_location(&self, location);
}
}
self
}
pub fn add_record(&self, block: &mut Block, record: InstructionDebugRecordData) {
unsafe {
block.builder.add_instruction_record(self, record);
}
}
}
#[derive(Clone)]
pub struct InstructionData {
kind: Instr,
@ -355,6 +339,7 @@ pub enum CmpPredicate {
NE,
}
/// https://llvm.org/docs/LangRef.html#instruction-reference
#[derive(Clone)]
pub enum Instr {
Param(usize),
@ -397,8 +382,56 @@ pub enum Instr {
/// Integer Comparison
ICmp(CmpPredicate, InstructionValue, InstructionValue),
/// FLoat Comparison
FCmp(CmpPredicate, InstructionValue, InstructionValue),
/// The `trunc` instruction truncates the high order bits in value and
/// converts the remaining bits to ty2. Since the source size must be larger
/// than the destination size, `trunc` cannot be a no-op cast. It will
/// always truncate bits.
Trunc(InstructionValue, Type),
/// The `zext` fills the high order bits of the value with zero bits until
/// it reaches the size of the destination type, ty2.
ZExt(InstructionValue, Type),
/// The `sext` instruction performs a sign extension by copying the sign bit
/// (highest order bit) of the value until it reaches the bit size of the
/// type ty2.
SExt(InstructionValue, Type),
/// The `fptrunc` instruction casts a value from a larger floating-point
/// type to a smaller floating-point type.
FPTrunc(InstructionValue, Type),
/// The `fpext` instruction extends the value from a smaller floating-point
/// type to a larger floating-point type.
FPExt(InstructionValue, Type),
/// The `fptoui` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToUI(InstructionValue, Type),
/// The `fptosi` instruction takes a value to cast, which must be a scalar
/// or vector floating-point value, and a type to cast it to ty2, which must
/// be an integer type.
FPToSI(InstructionValue, Type),
/// The `uitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type.
UIToFP(InstructionValue, Type),
/// The `sitofp` instruction takes a value to cast, which must be a scalar
/// or vector integer value, and a type to cast it to ty2, which must be an
/// floating-point type
SIToFP(InstructionValue, Type),
/// The `ptrtoint` instruction converts value to integer type ty2 by
/// interpreting the all pointer representation bits as an integer
/// (equivalent to a bitcast) and either truncating or zero extending that
/// value to the size of the integer type.
PtrToInt(InstructionValue, Type),
/// The `inttoptr` instruction converts value to type ty2 by applying either
/// a zero extension or a truncation depending on the size of the integer
/// value.
IntToPtr(InstructionValue, Type),
/// The `bitcast` instruction converts value to type ty2. It is always a
/// no-op cast because no bits change with this conversion.
BitCast(InstructionValue, Type),
FunctionCall(FunctionValue, Vec<InstructionValue>),
}
@ -473,84 +506,6 @@ pub enum CustomTypeKind {
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
pub struct NamedStruct(pub String, pub Vec<Type>);
impl InstructionValue {
pub(crate) fn get_type(&self, builder: &Builder) -> Result<Type, ()> {
use Instr::*;
unsafe {
match &builder.instr_data(self).kind {
Param(nth) => builder
.function_data(&self.0.0)
.params
.get(*nth)
.cloned()
.ok_or(()),
Constant(c) => Ok(c.get_type()),
Add(lhs, rhs) => match_types(lhs, rhs, &builder),
FAdd(lhs, rhs) => match_types(lhs, rhs, &builder),
Sub(lhs, rhs) => match_types(lhs, rhs, &builder),
FSub(lhs, rhs) => match_types(lhs, rhs, &builder),
Mul(lhs, rhs) => match_types(lhs, rhs, &builder),
FMul(lhs, rhs) => match_types(lhs, rhs, &builder),
UDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
SDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
FDiv(lhs, rhs) => match_types(lhs, rhs, &builder),
URem(lhs, rhs) => match_types(lhs, rhs, &builder),
SRem(lhs, rhs) => match_types(lhs, rhs, &builder),
FRem(lhs, rhs) => match_types(lhs, rhs, &builder),
And(lhs, rhs) => match_types(lhs, rhs, &builder),
ICmp(_, _, _) => Ok(Type::Bool),
FCmp(_, _, _) => Ok(Type::Bool),
FunctionCall(function_value, _) => Ok(builder.function_data(function_value).ret),
Phi(values) => values.first().ok_or(()).and_then(|v| v.get_type(&builder)),
Alloca(ty) => Ok(Type::Ptr(Box::new(ty.clone()))),
Load(_, ty) => Ok(ty.clone()),
Store(_, value) => value.get_type(builder),
ArrayAlloca(ty, _) => Ok(Type::Ptr(Box::new(ty.clone()))),
GetElemPtr(instr, _) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = &instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
match *inner_ty.clone() {
Type::Array(elem_ty, _) => Ok(Type::Ptr(Box::new(*elem_ty.clone()))),
_ => Ok(instr_ty),
}
}
GetStructElemPtr(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
let Type::Ptr(inner_ty) = instr_ty else {
panic!("GetStructElemPtr on non-pointer! ({:?})", &instr_ty)
};
let Type::CustomType(ty_value) = *inner_ty else {
panic!("GetStructElemPtr on non-struct! ({:?})", &inner_ty)
};
let field_ty = match builder.type_data(&ty_value).kind {
CustomTypeKind::NamedStruct(NamedStruct(_, fields)) => {
fields.get_unchecked(*idx as usize).clone()
}
};
Ok(Type::Ptr(Box::new(field_ty)))
}
ExtractValue(instr, idx) => {
let instr_ty = instr.get_type(builder)?;
Ok(match instr_ty {
Type::CustomType(struct_ty) => {
let data = builder.type_data(&struct_ty);
match data.kind {
CustomTypeKind::NamedStruct(named_struct) => {
named_struct.1.get(*idx as usize).unwrap().clone()
}
}
}
Type::Array(elem_ty, _) => *elem_ty.clone(),
_ => return Err(()),
})
}
}
}
}
}
impl ConstValue {
pub fn get_type(&self) -> Type {
use Type::*;
@ -657,6 +612,63 @@ impl Type {
}
}
}
pub fn cast_instruction(&self, value: InstructionValue, other: &Type) -> Option<Instr> {
use Type::*;
match (self, other) {
(I8, I16 | I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I16, I32 | I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I32, I64 | I128) => Some(Instr::SExt(value, other.clone())),
(I64, I128) => Some(Instr::SExt(value, other.clone())),
(I128 | U128, I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
Some(Instr::Trunc(value, other.clone()))
}
(I64 | U64, I32 | U32 | I16 | U16 | I8 | U8) => {
Some(Instr::Trunc(value, other.clone()))
}
(I32 | U32, I16 | U16 | I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(I16 | U16, I8 | U8) => Some(Instr::Trunc(value, other.clone())),
(U8 | I8, U16 | I16 | U32 | I32 | U64 | I64 | U128 | I128) => {
Some(Instr::ZExt(value, other.clone()))
}
(U16 | I16, U32 | I32 | U64 | I64 | U128 | I128) => {
Some(Instr::ZExt(value, other.clone()))
}
(U32 | I32, U64 | I64 | U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U64 | I64, U128 | I128) => Some(Instr::ZExt(value, other.clone())),
(U8 | U16 | U32 | U64 | U128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::UIToFP(value, other.clone()))
}
(I8 | I16 | I32 | I64 | I128, F16 | F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::SIToFP(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, U8 | U16 | U32 | U64 | U128) => {
Some(Instr::FPToUI(value, other.clone()))
}
(F16 | F32 | F32B | F64 | F80 | F128 | F128PPC, I8 | I16 | I32 | I64 | I128) => {
Some(Instr::FPToSI(value, other.clone()))
}
(I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8, Ptr(_)) => {
Some(Instr::IntToPtr(value, other.clone()))
}
(Ptr(_), I128 | U128 | I64 | U64 | I32 | U32 | I16 | U16 | I8 | U8) => {
Some(Instr::PtrToInt(value, other.clone()))
}
(F16, F32 | F32B | F64 | F80 | F128 | F128PPC) => {
Some(Instr::FPExt(value, other.clone()))
}
(F32 | F32B, F64 | F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F64, F80 | F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F80, F128 | F128PPC) => Some(Instr::FPExt(value, other.clone())),
(F128PPC | F128, F80 | F64 | F32B | F32 | F16) => {
Some(Instr::FPTrunc(value, other.clone()))
}
(F80, F64 | F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F64, F32B | F32 | F16) => Some(Instr::FPTrunc(value, other.clone())),
(F32B | F32, F16) => Some(Instr::FPTrunc(value, other.clone())),
_ => None,
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord)]

View File

@ -256,7 +256,9 @@ impl ast::Expression {
Box::new(expr.process(module_id)),
),
},
ast::ExpressionKind::CastTo(expression, _) => todo!(),
ast::ExpressionKind::CastTo(expression, ty) => {
mir::ExprKind::CastTo(Box::new(expression.process(module_id)), ty.0.clone().into())
}
};
mir::Expression(kind, self.1.as_meta(module_id))

View File

@ -353,16 +353,16 @@ impl mir::Module {
// Codegen actual parameters
let arg_name = format!("arg.{}", p_name);
let param = entry
.build(format!("{}.get", arg_name), Instr::Param(i))
.build_named(format!("{}.get", arg_name), Instr::Param(i))
.unwrap();
let alloca = entry
.build(
.build_named(
&arg_name,
Instr::Alloca(p_ty.get_type(&type_values, &types)),
)
.unwrap();
entry
.build(format!("{}.store", arg_name), Instr::Store(alloca, param))
.build_named(format!("{}.store", arg_name), Instr::Store(alloca, param))
.unwrap();
stack_values.insert(
p_name.clone(),
@ -488,7 +488,7 @@ impl mir::Statement {
let alloca = scope
.block
.build(
.build_named(
name,
Instr::Alloca(value.1.get_type(scope.type_values, scope.types)),
)
@ -497,7 +497,7 @@ impl mir::Statement {
scope
.block
.build(
.build_named(
format!("{}.store", name),
Instr::Store(alloca, value.instr()),
)
@ -557,7 +557,7 @@ impl mir::Statement {
StackValueKind::Mutable(instr) => {
scope
.block
.build(backing_var, Instr::Store(instr, rhs_value.instr()))
.build_named(backing_var, Instr::Store(instr, rhs_value.instr()))
.unwrap()
.maybe_location(&mut scope.block, location);
}
@ -600,7 +600,7 @@ impl mir::Expression {
v.0.derive(
scope
.block
.build(
.build_named(
format!("{}", varref.1),
Instr::Load(
v.0.instr(),
@ -647,7 +647,7 @@ impl mir::Expression {
_ => todo!(),
};
Some(StackValue(
StackValueKind::Immutable(scope.block.build_anon(instr).unwrap()),
StackValueKind::Immutable(scope.block.build(instr).unwrap()),
TypeKind::U32,
))
}
@ -671,7 +671,7 @@ impl mir::Expression {
let val = scope
.block
.build(
.build_named(
call.name.clone(),
Instr::FunctionCall(callee.ir.value(), params),
)
@ -686,11 +686,11 @@ impl mir::Expression {
let ptr = if ret_type_kind != TypeKind::Void {
let ptr = scope
.block
.build(&call.name, Instr::Alloca(ret_type.clone()))
.build_named(&call.name, Instr::Alloca(ret_type.clone()))
.unwrap();
scope
.block
.build(format!("{}.store", call.name), Instr::Store(ptr, val))
.build_named(format!("{}.store", call.name), Instr::Store(ptr, val))
.unwrap();
Some(ptr)
@ -704,7 +704,7 @@ impl mir::Expression {
StackValueKind::Immutable(
scope
.block
.build(call.name.clone(), Instr::Load(ptr, ret_type))
.build_named(call.name.clone(), Instr::Load(ptr, ret_type))
.unwrap(),
),
ret_type_kind,
@ -743,7 +743,7 @@ impl mir::Expression {
let first = scope
.block
.build("array.zero", Instr::Constant(ConstValue::U32(0)))
.build_named("array.zero", Instr::Constant(ConstValue::U32(0)))
.unwrap();
let TypeKind::CodegenPtr(inner) = ty else {
@ -754,7 +754,7 @@ impl mir::Expression {
dbg!(&further_inner, &val_t);
let loaded = scope
.block
.build(
.build_named(
"array.load",
Instr::Load(
kind.instr(),
@ -765,7 +765,7 @@ impl mir::Expression {
(
scope
.block
.build(format!("array.gep"), Instr::GetElemPtr(loaded, vec![idx]))
.build_named(format!("array.gep"), Instr::GetElemPtr(loaded, vec![idx]))
.unwrap()
.maybe_location(&mut scope.block, location),
*further_inner,
@ -777,7 +777,7 @@ impl mir::Expression {
(
scope
.block
.build(
.build_named(
format!("array.gep"),
Instr::GetElemPtr(kind.instr(), vec![first, idx]),
)
@ -793,7 +793,7 @@ impl mir::Expression {
kind.derive(
scope
.block
.build(
.build_named(
"array.load",
Instr::Load(
ptr,
@ -837,7 +837,7 @@ impl mir::Expression {
let array = scope
.block
.build(&array_name, Instr::Alloca(array_ty.clone()))
.build_named(&array_name, Instr::Alloca(array_ty.clone()))
.unwrap()
.maybe_location(&mut scope.block, location);
@ -847,30 +847,30 @@ impl mir::Expression {
let index_expr = scope
.block
.build(
.build_named(
index.to_string(),
Instr::Constant(ConstValue::U32(index as u32)),
)
.unwrap();
let first = scope
.block
.build("zero", Instr::Constant(ConstValue::U32(0)))
.build_named("zero", Instr::Constant(ConstValue::U32(0)))
.unwrap();
let ptr = scope
.block
.build(gep_n, Instr::GetElemPtr(array, vec![first, index_expr]))
.build_named(gep_n, Instr::GetElemPtr(array, vec![first, index_expr]))
.unwrap()
.maybe_location(&mut scope.block, location);
scope
.block
.build(store_n, Instr::Store(ptr, *instr))
.build_named(store_n, Instr::Store(ptr, *instr))
.unwrap()
.maybe_location(&mut scope.block, location);
}
let array_val = scope
.block
.build(load_n, Instr::Load(array, array_ty))
.build_named(load_n, Instr::Load(array, array_ty))
.unwrap()
.maybe_location(&mut scope.block, location);
@ -897,7 +897,7 @@ impl mir::Expression {
let value = scope
.block
.build(
.build_named(
gep_n,
Instr::GetStructElemPtr(struct_val.instr(), idx as u32),
)
@ -910,7 +910,7 @@ impl mir::Expression {
struct_val.0.derive(
scope
.block
.build(
.build_named(
load_n,
Instr::Load(
value,
@ -937,7 +937,7 @@ impl mir::Expression {
let struct_ptr = scope
.block
.build(name, Instr::Alloca(struct_ty.clone()))
.build_named(name, Instr::Alloca(struct_ty.clone()))
.unwrap()
.maybe_location(&mut scope.block, location);
@ -947,13 +947,13 @@ impl mir::Expression {
let elem_ptr = scope
.block
.build(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
.build_named(gep_n, Instr::GetStructElemPtr(struct_ptr, i as u32))
.unwrap()
.maybe_location(&mut scope.block, location);
if let Some(val) = exp.codegen(scope, state) {
scope
.block
.build(store_n, Instr::Store(elem_ptr, val.instr()))
.build_named(store_n, Instr::Store(elem_ptr, val.instr()))
.unwrap()
.maybe_location(&mut scope.block, location);
}
@ -961,7 +961,7 @@ impl mir::Expression {
let struct_val = scope
.block
.build(load_n, Instr::Load(struct_ptr, struct_ty))
.build_named(load_n, Instr::Load(struct_ptr, struct_ty))
.unwrap();
Some(StackValue(
@ -993,7 +993,7 @@ impl mir::Expression {
let var_ptr_instr = scope
.block
.build(
.build_named(
format!("{}.deref", varref.1),
Instr::Load(
v.0.instr(),
@ -1009,7 +1009,7 @@ impl mir::Expression {
v.0.derive(
scope
.block
.build(
.build_named(
format!("{}.deref.inner", varref.1),
Instr::Load(
var_ptr_instr,
@ -1028,6 +1028,21 @@ impl mir::Expression {
}
})
}
mir::ExprKind::CastTo(expression, type_kind) => {
let val = expression.codegen(scope, state)?;
let cast_instr = val
.1
.get_type(scope.type_values, scope.types)
.cast_instruction(
val.instr(),
&type_kind.get_type(scope.type_values, scope.types),
)
.unwrap();
Some(StackValue(
val.0.derive(scope.block.build(cast_instr).unwrap()),
type_kind.clone(),
))
}
};
if let Some(value) = &value {
value.instr().maybe_location(&mut scope.block, location);
@ -1108,7 +1123,7 @@ impl mir::IfExpression {
incoming.extend(else_res.clone());
let instr = scope
.block
.build(
.build_named(
"phi",
Instr::Phi(incoming.iter().map(|i| i.instr()).collect()),
)
@ -1150,7 +1165,7 @@ impl mir::CmpOperator {
impl mir::Literal {
fn as_const(&self, block: &mut Block) -> InstructionValue {
block
.build(format!("{}", self), self.as_const_kind())
.build_named(format!("{}", self), self.as_const_kind())
.unwrap()
}

View File

@ -211,6 +211,7 @@ impl Display for ExprKind {
ExprKind::Borrow(var_ref, false) => write!(f, "&{}", var_ref),
ExprKind::Borrow(var_ref, true) => write!(f, "&mut {}", var_ref),
ExprKind::Deref(var_ref) => write!(f, "*{}", var_ref),
ExprKind::CastTo(expression, type_kind) => write!(f, "{} as {}", expression, type_kind),
}
}
}

View File

@ -14,6 +14,82 @@ pub enum ReturnTypeOther {
}
impl TypeKind {
pub fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
if self == other {
return Ok(self.clone());
}
match (self, other) {
(TypeKind::Vague(Vague::Integer), other) | (other, TypeKind::Vague(Vague::Integer)) => {
match other {
TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Integer)),
TypeKind::Vague(Vague::Integer) => Ok(TypeKind::Vague(Vague::Integer)),
TypeKind::I8
| TypeKind::I16
| TypeKind::I32
| TypeKind::I64
| TypeKind::I128
| TypeKind::U8
| TypeKind::U16
| TypeKind::U32
| TypeKind::U64
| TypeKind::U128 => Ok(other.clone()),
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
(TypeKind::Vague(Vague::Decimal), other) | (other, TypeKind::Vague(Vague::Decimal)) => {
match other {
TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::Vague(Vague::Decimal) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::F16
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
| TypeKind::F128
| TypeKind::F128PPC => Ok(other.clone()),
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
(TypeKind::Vague(Vague::Unknown), other) | (other, TypeKind::Vague(Vague::Unknown)) => {
Ok(other.clone())
}
(TypeKind::Borrow(val1, mut1), TypeKind::Borrow(val2, mut2)) => {
// Extracted to give priority for other collapse-error
let collapsed = val1.collapse_into(val2)?;
if mut1 == mut2 {
Ok(TypeKind::Borrow(Box::new(collapsed), *mut1 && *mut2))
} else {
Err(ErrorKind::TypesDifferMutability(
self.clone(),
other.clone(),
))
}
}
(TypeKind::UserPtr(val1), TypeKind::UserPtr(val2)) => {
Ok(TypeKind::UserPtr(Box::new(val1.collapse_into(val2)?)))
}
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
pub fn cast_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
if let Ok(collapsed) = self.collapse_into(other) {
Ok(collapsed)
} else {
let self_cat = self.category();
let other_cat = other.category();
match (self, other) {
(TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Ok(other.clone()),
_ => match (&self_cat, &other_cat) {
(TypeCategory::Integer, TypeCategory::Integer) => Ok(other.clone()),
(TypeCategory::Real, TypeCategory::Real) => Ok(other.clone()),
_ => Err(ErrorKind::NotCastableTo(self.clone(), other.clone())),
},
}
}
}
/// Return the type that is the result of a binary operator between two
/// values of this type
pub fn binop_type(&self, op: &BinaryOperator) -> TypeKind {
@ -159,6 +235,49 @@ impl TypeKind {
_ => true,
}
}
pub fn category(&self) -> TypeCategory {
match self {
TypeKind::I8
| TypeKind::I16
| TypeKind::I32
| TypeKind::I64
| TypeKind::I128
| TypeKind::U8
| TypeKind::U16
| TypeKind::U32
| TypeKind::U64
| TypeKind::U128
| TypeKind::Str => TypeCategory::Integer,
TypeKind::F16
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F128
| TypeKind::F80
| TypeKind::F128PPC => TypeCategory::Real,
TypeKind::Void => TypeCategory::Other,
TypeKind::Bool => TypeCategory::Other,
TypeKind::Array(_, _) => TypeCategory::Other,
TypeKind::CustomType(_) => TypeCategory::Other,
TypeKind::Borrow(_, _) => TypeCategory::Other,
TypeKind::UserPtr(_) => TypeCategory::Other,
TypeKind::CodegenPtr(_) => TypeCategory::Other,
TypeKind::Vague(vague_type) => match vague_type {
VagueType::Unknown => TypeCategory::Other,
VagueType::Integer => TypeCategory::Integer,
VagueType::Decimal => TypeCategory::Real,
VagueType::TypeRef(_) => TypeCategory::TypeRef,
},
}
}
}
pub enum TypeCategory {
Integer,
Real,
Other,
TypeRef,
}
impl StructType {
@ -319,6 +438,13 @@ impl Expression {
_ => Err(ReturnTypeOther::DerefNonBorrow(var.2)),
}
}
CastTo(expr, type_kind) => match expr.return_type(refs) {
Ok(ret_type) => match ret_type {
(ReturnKind::Hard, ty) => Ok((ReturnKind::Hard, ty)),
_ => Ok((ReturnKind::Soft, type_kind.clone())),
},
Err(_) => Ok((ReturnKind::Soft, type_kind.clone())),
},
}
}
@ -336,6 +462,7 @@ impl Expression {
ExprKind::BinOp(_, _, _) => None,
ExprKind::FunctionCall(_) => None,
ExprKind::If(_) => None,
ExprKind::CastTo(expression, _) => expression.backing_var(),
}
}
@ -363,6 +490,7 @@ impl Expression {
ExprKind::Block(_) => None,
ExprKind::Borrow(_, _) => None,
ExprKind::Deref(_) => None,
ExprKind::CastTo(expression, _) => expression.num_value(),
}
}
}
@ -458,73 +586,6 @@ impl TypeKind {
}
}
pub trait Collapsable: Sized + Clone {
/// Try to narrow two types into one singular type. E.g. Vague(Number) and
/// I32 could be narrowed to just I32.
fn collapse_into(&self, other: &Self) -> Result<Self, ErrorKind>;
}
impl Collapsable for TypeKind {
fn collapse_into(&self, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
if self == other {
return Ok(self.clone());
}
match (self, other) {
(TypeKind::Vague(Vague::Integer), other) | (other, TypeKind::Vague(Vague::Integer)) => {
match other {
TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Integer)),
TypeKind::Vague(Vague::Integer) => Ok(TypeKind::Vague(Vague::Integer)),
TypeKind::I8
| TypeKind::I16
| TypeKind::I32
| TypeKind::I64
| TypeKind::I128
| TypeKind::U8
| TypeKind::U16
| TypeKind::U32
| TypeKind::U64
| TypeKind::U128 => Ok(other.clone()),
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
(TypeKind::Vague(Vague::Decimal), other) | (other, TypeKind::Vague(Vague::Decimal)) => {
match other {
TypeKind::Vague(Vague::Unknown) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::Vague(Vague::Decimal) => Ok(TypeKind::Vague(Vague::Decimal)),
TypeKind::F16
| TypeKind::F32B
| TypeKind::F32
| TypeKind::F64
| TypeKind::F80
| TypeKind::F128
| TypeKind::F128PPC => Ok(other.clone()),
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
(TypeKind::Vague(Vague::Unknown), other) | (other, TypeKind::Vague(Vague::Unknown)) => {
Ok(other.clone())
}
(TypeKind::Borrow(val1, mut1), TypeKind::Borrow(val2, mut2)) => {
// Extracted to give priority for other collapse-error
let collapsed = val1.collapse_into(val2)?;
if mut1 == mut2 {
Ok(TypeKind::Borrow(Box::new(collapsed), *mut1 && *mut2))
} else {
Err(ErrorKind::TypesDifferMutability(
self.clone(),
other.clone(),
))
}
}
(TypeKind::UserPtr(val1), TypeKind::UserPtr(val2)) => {
Ok(TypeKind::UserPtr(Box::new(val1.collapse_into(val2)?)))
}
_ => Err(ErrorKind::TypesIncompatible(self.clone(), other.clone())),
}
}
}
impl Literal {
pub fn num_value(&self) -> Option<i128> {
match self {
@ -553,22 +614,6 @@ impl Literal {
}
}
impl Collapsable for ScopeFunction {
fn collapse_into(&self, other: &ScopeFunction) -> Result<ScopeFunction, ErrorKind> {
Ok(ScopeFunction {
ret: self.ret.collapse_into(&other.ret)?,
params: try_all(
self.params
.iter()
.zip(&other.params)
.map(|(p1, p2)| p1.collapse_into(&p2))
.collect(),
)
.map_err(|e| e.first().unwrap().clone())?,
})
}
}
#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq, PartialOrd, Ord)]
pub enum EqualsIssue {
#[error("Function is already defined locally at {:?}", (.0).range)]

View File

@ -72,7 +72,7 @@ impl<'map> Pass for LinkerPass<'map> {
type Data = ();
type TError = ErrorKind;
fn context(&mut self, context: &mut Context, mut state: LinkerPassState) -> PassResult {
let mut mains = context
let mains = context
.modules
.iter_mut()
.filter(|m| m.is_main)

View File

@ -254,6 +254,7 @@ pub enum ExprKind {
Block(Block),
Borrow(NamedVariableRef, bool),
Deref(NamedVariableRef),
CastTo(Box<Expression>, TypeKind),
}
#[derive(Debug)]

View File

@ -6,7 +6,6 @@ use crate::{mir::*, util::try_all};
use VagueType as Vague;
use super::{
implement::Collapsable,
pass::{Pass, PassResult, PassState, ScopeVariable},
typerefs::TypeRefs,
};
@ -67,6 +66,8 @@ pub enum ErrorKind {
ImpossibleMutLet(String),
#[error("Cannot produce a negative unsigned value of type {0}!")]
NegativeUnsignedValue(TypeKind),
#[error("Cannot cast type {0} into type {1}!")]
NotCastableTo(TypeKind, TypeKind),
}
/// Struct used to implement a type-checking pass that can be performed on the
@ -712,6 +713,10 @@ impl Expression {
Ok(*inner)
}
ExprKind::CastTo(expression, type_kind) => {
let expr = expression.typecheck(state, typerefs, None)?;
expr.resolve_ref(typerefs).cast_into(type_kind)
}
}
}
}

View File

@ -397,6 +397,10 @@ impl Expression {
_ => Err(ErrorKind::AttemptedDerefNonBorrow(var.1.clone())),
}
}
ExprKind::CastTo(expression, type_kind) => {
expression.infer_types(state, type_refs)?;
Ok(type_refs.from_type(type_kind).unwrap())
}
}
}
}

View File

@ -6,7 +6,7 @@ use std::{
use crate::mir::VagueType;
use super::{implement::Collapsable, typecheck::ErrorKind, BinaryOperator, TypeKind};
use super::{typecheck::ErrorKind, BinaryOperator, TypeKind};
#[derive(Clone)]
pub struct TypeRef<'scope>(TypeIdRef, &'scope ScopeTypeRefs<'scope>);

11
reid_src/cast.reid Normal file
View File

@ -0,0 +1,11 @@
// Arithmetic, function calls and imports!
fn other() -> i16 {
return 6;
}
fn main() -> u32 {
let value = other() as u32;
return value;
}