From 75a08622e8754dbc43ccd2f08031227527645d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 25 Jul 2013 10:51:45 +0200 Subject: [PATCH 1/3] Add a function to check whether a ValueRef is a constant --- src/librustc/middle/trans/common.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 713939a5d8319..4099f64263703 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -850,6 +850,12 @@ pub fn const_get_elt(cx: &CrateContext, v: ValueRef, us: &[c_uint]) } } +pub fn is_const(v: ValueRef) -> bool { + unsafe { + llvm::LLVMIsConstant(v) == True + } +} + pub fn const_to_int(v: ValueRef) -> c_longlong { unsafe { llvm::LLVMConstIntGetSExtValue(v) From e0685e22a3a7a5735f635bd28bdcb5084dd5be34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 25 Jul 2013 10:53:27 +0200 Subject: [PATCH 2/3] Make omission of impossible if-else branches work with constants Until now, we only optimized away impossible branches when there is a literal true/false in the code. But since the LLVM IR builder already does constant folding for us, we can trivially expand that to work with constants as well. Refs #7834 --- src/librustc/middle/trans/controlflow.rs | 58 +++++++++++------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 2b6c1d8241860..a00cd94ad54ef 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -65,44 +65,38 @@ pub fn trans_if(bcx: @mut Block, let _icx = push_ctxt("trans_if"); - match cond.node { - // `if true` and `if false` can be trans'd more efficiently, - // by dropping branches that are known to be impossible. - ast::expr_lit(@ref l) => match l.node { - ast::lit_bool(true) => { - // if true { .. } [else { .. }] - let then_bcx_in = scope_block(bcx, thn.info(), "if_true_then"); - let then_bcx_out = trans_block(then_bcx_in, thn, dest); - let then_bcx_out = trans_block_cleanups(then_bcx_out, - block_cleanups(then_bcx_in)); - Br(bcx, then_bcx_in.llbb); - return then_bcx_out; - } - ast::lit_bool(false) => { - match els { - // if false { .. } else { .. } - Some(elexpr) => { - let (else_bcx_in, else_bcx_out) = - trans_if_else(bcx, elexpr, dest, "if_false_else"); - Br(bcx, else_bcx_in.llbb); - return else_bcx_out; - } - // if false { .. } - None => return bcx, + let Result {bcx, val: cond_val} = + expr::trans_to_datum(bcx, cond).to_result(); + + let cond_val = bool_to_i1(bcx, cond_val); + + // Drop branches that are known to be impossible + if is_const(cond_val) && !is_undef(cond_val) { + if const_to_uint(cond_val) == 1 { + // if true { .. } [else { .. }] + let then_bcx_in = scope_block(bcx, thn.info(), "if_true_then"); + let then_bcx_out = trans_block(then_bcx_in, thn, dest); + let then_bcx_out = trans_block_cleanups(then_bcx_out, + block_cleanups(then_bcx_in)); + Br(bcx, then_bcx_in.llbb); + return then_bcx_out; + } else { + match els { + // if false { .. } else { .. } + Some(elexpr) => { + let (else_bcx_in, else_bcx_out) = + trans_if_else(bcx, elexpr, dest, "if_false_else"); + Br(bcx, else_bcx_in.llbb); + return else_bcx_out; } + // if false { .. } + None => return bcx, } - _ => {} - }, - _ => {} + } } - let Result {bcx, val: cond_val} = - expr::trans_to_datum(bcx, cond).to_result(); - let then_bcx_in = scope_block(bcx, thn.info(), "then"); - let cond_val = bool_to_i1(bcx, cond_val); - let then_bcx_out = trans_block(then_bcx_in, thn, dest); let then_bcx_out = trans_block_cleanups(then_bcx_out, block_cleanups(then_bcx_in)); From 7078ab7324aef0ad04b4a79a514f2c1d59b73fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Steinbrink?= Date: Thu, 25 Jul 2013 17:03:46 +0200 Subject: [PATCH 3/3] Generate branchless code when "if" can be evaluated at compile time We already avoid the conditional branch, but still have an unconditional branch in the generated code. Remove it. --- src/librustc/middle/trans/controlflow.rs | 32 +++++++++++------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index a00cd94ad54ef..8c87766bbbe1f 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -74,20 +74,18 @@ pub fn trans_if(bcx: @mut Block, if is_const(cond_val) && !is_undef(cond_val) { if const_to_uint(cond_val) == 1 { // if true { .. } [else { .. }] - let then_bcx_in = scope_block(bcx, thn.info(), "if_true_then"); - let then_bcx_out = trans_block(then_bcx_in, thn, dest); - let then_bcx_out = trans_block_cleanups(then_bcx_out, - block_cleanups(then_bcx_in)); - Br(bcx, then_bcx_in.llbb); - return then_bcx_out; + return do with_scope(bcx, thn.info(), "if_true_then") |bcx| { + let bcx_out = trans_block(bcx, thn, dest); + trans_block_cleanups(bcx_out, block_cleanups(bcx)) + } } else { match els { // if false { .. } else { .. } Some(elexpr) => { - let (else_bcx_in, else_bcx_out) = - trans_if_else(bcx, elexpr, dest, "if_false_else"); - Br(bcx, else_bcx_in.llbb); - return else_bcx_out; + return do with_scope(bcx, elexpr.info(), "if_false_then") |bcx| { + let bcx_out = trans_if_else(bcx, elexpr, dest); + trans_block_cleanups(bcx_out, block_cleanups(bcx)) + } } // if false { .. } None => return bcx, @@ -107,7 +105,8 @@ pub fn trans_if(bcx: @mut Block, // 'else' context let (else_bcx_in, next_bcx) = match els { Some(elexpr) => { - let (else_bcx_in, else_bcx_out) = trans_if_else(bcx, elexpr, dest, "else"); + let else_bcx_in = scope_block(bcx, elexpr.info(), "else"); + let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest); (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out])) } _ => { @@ -125,9 +124,8 @@ pub fn trans_if(bcx: @mut Block, return next_bcx; // trans `else [ if { .. } ... | { .. } ]` - fn trans_if_else(bcx: @mut Block, elexpr: @ast::expr, - dest: expr::Dest, scope_name: &str) -> (@mut Block, @mut Block) { - let else_bcx_in = scope_block(bcx, elexpr.info(), scope_name); + fn trans_if_else(else_bcx_in: @mut Block, elexpr: @ast::expr, + dest: expr::Dest) -> @mut Block { let else_bcx_out = match elexpr.node { ast::expr_if(_, _, _) => { let elseif_blk = ast_util::block_from_expr(elexpr); @@ -137,11 +135,9 @@ pub fn trans_if(bcx: @mut Block, trans_block(else_bcx_in, blk, dest) } // would be nice to have a constraint on ifs - _ => bcx.tcx().sess.bug("strange alternative in if") + _ => else_bcx_in.tcx().sess.bug("strange alternative in if") }; - let else_bcx_out = trans_block_cleanups(else_bcx_out, - block_cleanups(else_bcx_in)); - (else_bcx_in, else_bcx_out) + trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in)) } }