Compare commits
5 Commits
c4ab4ac0b3
...
c03a5188ea
Author | SHA1 | Date | |
---|---|---|---|
c03a5188ea | |||
e73b939de0 | |||
a6c9752883 | |||
104205ee5d | |||
3378f556ec |
@ -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();
|
||||
|
||||
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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))
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)]
|
||||
|
@ -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)
|
||||
|
@ -254,6 +254,7 @@ pub enum ExprKind {
|
||||
Block(Block),
|
||||
Borrow(NamedVariableRef, bool),
|
||||
Deref(NamedVariableRef),
|
||||
CastTo(Box<Expression>, TypeKind),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
11
reid_src/cast.reid
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user