Skip to content

Commit

Permalink
Add conversions from LLVM dialect to inkwell LLVM
Browse files Browse the repository at this point in the history
  • Loading branch information
vaivaswatha committed Jul 2, 2024
1 parent 30ebc17 commit 38e6868
Show file tree
Hide file tree
Showing 14 changed files with 1,074 additions and 115 deletions.
12 changes: 10 additions & 2 deletions pliron-llvm/src/bin/llvm-opt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use std::{path::PathBuf, process::ExitCode};
use inkwell::{context::Context as IWContext, module::Module as IWModule};

use clap::Parser;
use pliron::{arg_error_noloc, context::Context, printable::Printable, result::Result};
use pliron_llvm::from_inkwell;
use pliron::{
arg_error_noloc, context::Context, printable::Printable, result::Result, verify_error_noloc,
};
use pliron_llvm::{from_inkwell, to_inkwell};

#[derive(Parser)]
#[command(version, about="LLVM Optimizer", long_about = None)]
Expand All @@ -30,6 +32,12 @@ fn run(cli: Cli, ctx: &mut Context) -> Result<()> {
let pliron_module = from_inkwell::convert_module(ctx, &module)?;
println!("{}", pliron_module.disp(ctx));

let iwctx = &IWContext::create();
let module = to_inkwell::convert_module(ctx, iwctx, pliron_module)?;
module
.verify()
.map_err(|err| verify_error_noloc!("{}", err.to_string()))?;

if cli.text_output {
module
.print_to_file(&cli.output)
Expand Down
65 changes: 46 additions & 19 deletions pliron-llvm/src/from_inkwell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use inkwell::{
module::Module as IWModule,
types::{AnyType, AnyTypeEnum},
values::{
AnyValue, AnyValueEnum, BasicValueEnum, FunctionValue, InstructionOpcode, InstructionValue,
PhiValue,
AnyValue, AnyValueEnum, BasicValue, BasicValueEnum, FunctionValue, InstructionOpcode,
InstructionValue, PhiValue,
},
IntPredicate,
};
Expand All @@ -19,9 +19,10 @@ use pliron::{
attributes::IntegerAttr,
op_interfaces::{OneRegionInterface, OneResultInterface, SingleBlockRegionInterface},
ops::{FuncOp, ModuleOp},
types::{FunctionType, IntegerType, Signedness},
types::{FunctionType, IntegerType},
},
context::{Context, Ptr},
identifier::Identifier,
input_err_noloc, input_error_noloc,
op::Op,
operation::Operation,
Expand All @@ -38,7 +39,7 @@ use crate::{
ops::{
AShrOp, AddOp, AllocaOp, AndOp, BitcastOp, BrOp, CondBrOp, ConstantOp, ICmpOp, LShrOp,
LoadOp, MulOp, OrOp, ReturnOp, SDivOp, SRemOp, ShlOp, StoreOp, SubOp, UDivOp, URemOp,
XorOp,
UndefOp, XorOp,
},
types::{ArrayType, PointerType, StructErr, StructType, VoidType},
};
Expand All @@ -57,7 +58,7 @@ pub fn convert_type(ctx: &mut Context, ty: &AnyTypeEnum) -> Result<Ptr<TypeObj>>
match ty {
AnyTypeEnum::ArrayType(aty) => {
let elem = convert_type(ctx, &aty.get_element_type().as_any_type_enum())?;
Ok(ArrayType::get(ctx, elem, aty.len() as usize).into())
Ok(ArrayType::get(ctx, elem, aty.len() as u64).into())
}
AnyTypeEnum::FloatType(_fty) => {
todo!()
Expand Down Expand Up @@ -89,14 +90,16 @@ pub fn convert_type(ctx: &mut Context, ty: &AnyTypeEnum) -> Result<Ptr<TypeObj>>
let Some(name) = sty.get_name() else {
return input_err_noloc!(StructErr::OpaqueAndAnonymousErr);
};
Ok(StructType::get_named(ctx, name.to_str_res()?, None)?.into())
let name: Identifier = name.to_str_res()?.try_into()?;
Ok(StructType::get_named(ctx, name, None)?.into())
} else {
let field_types: Vec<_> = sty
.get_field_types_iter()
.map(|ty| convert_type(ctx, &ty.as_any_type_enum()))
.collect::<Result<_>>()?;
if let Some(name) = sty.get_name() {
Ok(StructType::get_named(ctx, name.to_str_res()?, Some(field_types))?.into())
let name: Identifier = name.to_str_res()?.try_into()?;
Ok(StructType::get_named(ctx, name, Some(field_types))?.into())
} else {
Ok(StructType::get_unnamed(ctx, field_types).into())
}
Expand Down Expand Up @@ -231,15 +234,27 @@ fn process_constant<'ctx>(
BasicValueEnum::IntValue(iv) if iv.is_constant_int() => {
// TODO: Zero extend or sign extend?
let u64 = iv.get_zero_extended_constant().unwrap();
let u64_ty = IntegerType::get(ctx, iv.get_type().get_bit_width(), Signedness::Signless);
let val_attr = IntegerAttr::new(u64_ty, ApInt::from_u64(u64));
let int_ty = TypePtr::<IntegerType>::from_ptr(
convert_type(ctx, &iv.get_type().as_any_type_enum())?,
ctx,
)?;
let val_attr = IntegerAttr::new(int_ty, ApInt::from_u64(u64));
let const_op = ConstantOp::new(ctx, Box::new(val_attr));
// Insert at the beginning of the entry block.
const_op
.get_operation()
.insert_at_front(cctx.entry_block, ctx);
cctx.value_map.insert(any_val, const_op.get_result(ctx));
}
BasicValueEnum::IntValue(iv) if iv.is_undef() => {
let int_ty = convert_type(ctx, &iv.get_type().as_any_type_enum())?;
let undef_op = UndefOp::new(ctx, int_ty);
// Insert at the beginning of the entry block.
undef_op
.get_operation()
.insert_at_front(cctx.entry_block, ctx);
cctx.value_map.insert(any_val, undef_op.get_result(ctx));
}
BasicValueEnum::FloatValue(fv) if fv.is_const() => todo!(),
BasicValueEnum::PointerValue(pv) if pv.is_const() => todo!(),
BasicValueEnum::StructValue(_sv) => todo!(),
Expand Down Expand Up @@ -286,10 +301,11 @@ fn get_operand<T: Clone>(opds: &[T], idx: usize) -> Result<T> {
}

/// Compute the arguments to be passed when branching from `src` to `dest`.
fn convert_branch_args(
cctx: &ConversionContext,
src_block: IWBasicBlock,
dst_block: IWBasicBlock,
fn convert_branch_args<'ctx>(
ctx: &mut Context,
cctx: &mut ConversionContext<'ctx>,
src_block: IWBasicBlock<'ctx>,
dst_block: IWBasicBlock<'ctx>,
) -> Result<Vec<Value>> {
let mut args = vec![];
for inst in dst_block.get_instructions() {
Expand All @@ -301,6 +317,7 @@ fn convert_branch_args(
src_block.get_name().to_str_res().unwrap().to_string()
));
};
process_constant(ctx, cctx, incoming_val.as_basic_value_enum())?;
let Some(m_incoming_val) = cctx.value_map.get(&incoming_val.as_any_value_enum()) else {
return input_err_noloc!(ConversionErr::UndefinedValue(
incoming_val
Expand Down Expand Up @@ -358,26 +375,29 @@ fn convert_instruction<'ctx>(
"Conditional branch must have two successors"
);
let true_dest_opds = convert_branch_args(
ctx,
cctx,
inst.get_parent().unwrap(),
inst.get_operand(1).unwrap().unwrap_right(),
inst.get_operand(2).unwrap().unwrap_right(),
)?;
let false_dest_opds = convert_branch_args(
ctx,
cctx,
inst.get_parent().unwrap(),
inst.get_operand(2).unwrap().unwrap_right(),
inst.get_operand(1).unwrap().unwrap_right(),
)?;
Ok(CondBrOp::new(
ctx,
get_operand(opds, 0)?,
get_operand(succs, 0)?,
true_dest_opds,
get_operand(succs, 1)?,
true_dest_opds,
get_operand(succs, 0)?,
false_dest_opds,
)
.get_operation())
} else {
let dest_opds = convert_branch_args(
ctx,
cctx,
inst.get_parent().unwrap(),
inst.get_operand(0).unwrap().unwrap_right(),
Expand Down Expand Up @@ -445,7 +465,14 @@ fn convert_instruction<'ctx>(
}
InstructionOpcode::PtrToInt => todo!(),
InstructionOpcode::Resume => todo!(),
InstructionOpcode::Return => Ok(ReturnOp::new(ctx, get_operand(opds, 0)?).get_operation()),
InstructionOpcode::Return => {
let retval = if inst.get_num_operands() == 1 {
Some(get_operand(opds, 0)?)
} else {
None
};
Ok(ReturnOp::new(ctx, retval).get_operation())
}
InstructionOpcode::SDiv => {
let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
Ok(SDivOp::new(ctx, lhs, rhs).get_operation())
Expand Down Expand Up @@ -502,7 +529,7 @@ fn convert_block<'ctx>(
) -> Result<()> {
for inst in block.get_instructions() {
let inst_val = inst.as_any_value_enum();
if inst_val.is_phi_value() {
if inst.get_opcode() == InstructionOpcode::Phi {
let ty = convert_type(ctx, &inst.get_type().as_any_type_enum())?;
let arg_idx = m_block.deref_mut(ctx).add_argument(ty);
cctx.value_map
Expand Down
1 change: 1 addition & 0 deletions pliron-llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod attributes;
pub mod from_inkwell;
pub mod op_interfaces;
pub mod ops;
pub mod to_inkwell;
pub mod types;

/// Register LLVM dialect, its ops, types and attributes into context.
Expand Down
71 changes: 67 additions & 4 deletions pliron-llvm/src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,28 @@ use super::{
#[def_op("llvm.return")]
pub struct ReturnOp {}
impl ReturnOp {
pub fn new(ctx: &mut Context, value: Value) -> Self {
let op = Operation::new(ctx, Self::get_opid_static(), vec![], vec![value], vec![], 0);
/// Create a new [ReturnOp]
pub fn new(ctx: &mut Context, value: Option<Value>) -> Self {
let op = Operation::new(
ctx,
Self::get_opid_static(),
vec![],
value.into_iter().collect(),
vec![],
0,
);
ReturnOp { op }
}

/// Get the returned value, if it exists.
pub fn retval(&self, ctx: &Context) -> Option<Value> {
let op = &*self.get_operation().deref(ctx);
if op.get_num_operands() == 1 {
op.get_operand(0)
} else {
None
}
}
}
impl_canonical_syntax!(ReturnOp);
impl_verify_succ!(ReturnOp);
Expand Down Expand Up @@ -242,6 +260,16 @@ impl ICmpOp {
.set(icmp_op::ATTR_KEY_PREDICATE.clone(), pred);
ICmpOp { op }
}

/// Get the predicate
pub fn predicate(&self, ctx: &Context) -> ICmpPredicateAttr {
self.get_operation()
.deref(ctx)
.attributes
.get::<ICmpPredicateAttr>(&icmp_op::ATTR_KEY_PREDICATE)
.unwrap()
.clone()
}
}

impl Verify for ICmpOp {
Expand Down Expand Up @@ -330,7 +358,7 @@ impl Verify for AllocaOp {
impl_op_interface!(OneResultInterface for AllocaOp {});
impl_op_interface!(OneOpdInterface for AllocaOp {});
impl_op_interface!(PointerTypeResult for AllocaOp {
fn result_pointee_type(&self,ctx: &Context) -> Ptr<TypeObj> {
fn result_pointee_type(&self, ctx: &Context) -> Ptr<TypeObj> {
self.op
.deref(ctx)
.attributes
Expand Down Expand Up @@ -478,6 +506,11 @@ impl CondBrOp {
),
}
}

/// Get the condition value for the branch.
pub fn condition(&self, ctx: &Context) -> Value {
self.op.deref(ctx).get_operand(0).unwrap()
}
}
impl_canonical_syntax!(CondBrOp);
impl_verify_succ!(CondBrOp);
Expand Down Expand Up @@ -655,7 +688,7 @@ impl GetElementPtrOp {
let GepIndex::Constant(i) = idx else {
return arg_err_noloc!(GetElementPtrOpErr::IndicesErr);
};
if i as usize >= st.num_fields() {
if st.is_opaque() || i as usize >= st.num_fields() {
return arg_err_noloc!(GetElementPtrOpErr::IndicesErr);
}
indexed_type_inner(ctx, st.field_type(i as usize), idx_itr)
Expand Down Expand Up @@ -877,6 +910,35 @@ impl CallOpInterface for CallOp {
impl_canonical_syntax!(CallOp);
impl_verify_succ!(CallOp);

/// Undefined value of a type.
/// See MLIR's [llvm.mlir.undef](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirundef-llvmundefop).
///
/// Results:
///
/// | result | description |
/// |-----|-------|
/// | `result` | any type |
#[def_op("llvm.undef")]
pub struct UndefOp {}
impl_canonical_syntax!(UndefOp);
impl_verify_succ!(UndefOp);
impl_op_interface!(OneResultInterface for UndefOp {});

impl UndefOp {
/// Create a new [UndefOp].
pub fn new(ctx: &mut Context, result_ty: Ptr<TypeObj>) -> Self {
let op = Operation::new(
ctx,
Self::get_opid_static(),
vec![result_ty],
vec![],
vec![],
0,
);
UndefOp { op }
}
}

/// Numeric constant.
/// See MLIR's [llvm.mlir.constant](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirconstant-llvmconstantop).
///
Expand Down Expand Up @@ -977,5 +1039,6 @@ pub fn register(ctx: &mut Context) {
StoreOp::register(ctx, StoreOp::parser_fn);
CallOp::register(ctx, CallOp::parser_fn);
ConstantOp::register(ctx, ConstantOp::parser_fn);
UndefOp::register(ctx, UndefOp::parser_fn);
ReturnOp::register(ctx, ReturnOp::parser_fn);
}
Loading

0 comments on commit 38e6868

Please sign in to comment.