Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MIR pass to lower intrinsics #41024

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
// NB: if you’re adding an *optimisation* it ought to go to another set of passes
// in stage 4 below.
passes.push_hook(box mir::transform::dump_mir::DumpMir);
passes.push_pass(box mir::transform::lower_intrinsics::LowerIntrinsics);
passes.push_pass(box mir::transform::simplify::SimplifyCfg::new("initial"));
passes.push_pass(box mir::transform::qualify_consts::QualifyAndPromoteConstants);
passes.push_pass(box mir::transform::type_check::TypeckMir);
Expand Down
115 changes: 115 additions & 0 deletions src/librustc_mir/transform/lower_intrinsics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Some intrinsics can be lowered to MIR code. Doing this enables
//! more optimizations to happen.

use std::borrow::Cow;

use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource, Pass};
use rustc::ty::{TyCtxt, TypeVariants};
use syntax::abi::Abi;
use syntax_pos::symbol::InternedString;

pub struct LowerIntrinsics;

impl Pass for LowerIntrinsics {
fn name(&self) -> Cow<'static, str> { "LowerIntrinsics".into() }
}

impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _: MirSource, mir: &mut Mir<'tcx>) {
for bb_data in mir.basic_blocks_mut() {
if let Some((func, args, target)) = intrinsic_call(bb_data) {
lower_intrinsic(tcx, bb_data, func, args, target);
}
}
}
}

// Returns the elements of the function call in case it is an intrinsic
fn intrinsic_call<'tcx>(bb_data: &BasicBlockData<'tcx>)
-> Option<(Constant<'tcx>, Vec<Operand<'tcx>>, BasicBlock)> {
use self::TerminatorKind::Call;
match bb_data.terminator().kind {
Call { func: Operand::Constant(ref func), ref args, ref destination, .. } => {
// Note: for all intrinsics that we lower in this pass, we assume that they
// don't require cleanup. If an intrinsic happens to require cleanup,
// the cleanup code will be happily ignored and bad things will happen.

match func.ty.sty {
TypeVariants::TyFnDef(_, _, fn_sig)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and the following line look kind of funny. Is this the correct way to format it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I needed to break the line somewhere to avoid the 100 width limit. Maybe I could indent the if statement. I haven't seen a similar situation earlier, so I just went with what felt natural to me.

if fn_sig.skip_binder().abi == Abi::RustIntrinsic => {
let target = match destination {
&Some(ref d) => d.1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you do match *destnation to prevent &Some and &None?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the added value of that

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the preferred style

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, in that case I will change it. Thanks for pointing it out.

&None => {
// Ignore diverging intrinsics
return None
}
};

Some((func.clone(), args.clone(), target))
}
_ => None
}
}
_ => None
}
}

// Returns the name of the intrinsic that is being called
fn intrinsic_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, func: &Constant<'tcx>) -> InternedString {
let def_id = match func.literal {
Literal::Item { def_id, .. } => def_id,
Literal::Promoted { .. } => span_bug!(func.span, "promoted value with rust intrinsic abi"),
Literal::Value { ref value } => match value {
&ConstVal::Function(def_id, _) => def_id,
_ => span_bug!(func.span, "literal value with rust intrinsic abi"),
}
};

tcx.item_name(def_id).as_str()
}

fn lower_intrinsic<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
basic_block: &mut BasicBlockData<'tcx>,
func: Constant<'tcx>,
args: Vec<Operand<'tcx>>,
target: BasicBlock) {
let name = &*intrinsic_name(tcx, &func);
if name == "move_val_init" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this approach encourage a long if/else if chain for every intrinsic? If so, that just seems like a nightmare to read and maintain

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expect to introduce a match in the future and write a function for each intrinsic.

assert_eq!(args.len(), 2);

if let Operand::Consume(dest) = args[0].clone() {
// move_val_init(dest, src)
// =>
// Assign(dest, src)
let source_info = basic_block.terminator().source_info;
basic_block.statements.push(Statement {
source_info,
kind: StatementKind::Assign(
Lvalue::Projection(Box::new(Projection {
base: dest,
elem: ProjectionElem::Deref
})),
Rvalue::Use(args[1].clone())
)
});
} else {
bug!("destination argument not lvalue?");
}

// Since this is no longer a function call, replace the
// terminator with a simple Goto
basic_block.terminator_mut().kind = TerminatorKind::Goto { target };
}
}
1 change: 1 addition & 0 deletions src/librustc_mir/transform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub mod lower_intrinsics;
pub mod simplify_branches;
pub mod simplify;
pub mod erase_regions;
Expand Down
10 changes: 0 additions & 10 deletions src/librustc_trans/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,16 +418,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
};
let intrinsic = intrinsic.as_ref().map(|s| &s[..]);

if intrinsic == Some("move_val_init") {
let &(_, target) = destination.as_ref().unwrap();
// The first argument is a thin destination pointer.
let llptr = self.trans_operand(&bcx, &args[0]).immediate();
let val = self.trans_operand(&bcx, &args[1]);
self.store_operand(&bcx, llptr, None, val);
funclet_br(self, bcx, target);
return;
}

if intrinsic == Some("transmute") {
let &(ref dest, target) = destination.as_ref().unwrap();
self.trans_transmute(&bcx, &args[0], dest);
Expand Down