-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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) | ||
if fn_sig.skip_binder().abi == Abi::RustIntrinsic => { | ||
let target = match destination { | ||
&Some(ref d) => d.1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the added value of that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's the preferred style There was a problem hiding this comment. Choose a reason for hiding this commentThe 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" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will this approach encourage a long There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 }; | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.