Skip to content

Commit e75cf7e

Browse files
committed
Handle block expressions a little nicer.
This handles block expressions based on the kind of their trailing expression (if there is one), rather than always using DPS. This is to allow `unsafe { likely(foo) }` to work as expected, but also reduces the number of temporary allocas in some cases. Also adds a codegen test to make sure the intrinsics are being codegenned in a way that means they are having an effect. This commit also fixes a small bug I noticed in the translation of overflow checking that meant the branch hints weren't actually taking effect.
1 parent e74d5be commit e75cf7e

File tree

4 files changed

+138
-8
lines changed

4 files changed

+138
-8
lines changed

src/librustc_trans/trans/build.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@ pub fn terminate(cx: Block, _: &str) {
3131

3232
pub fn check_not_terminated(cx: Block) {
3333
if cx.terminated.get() {
34-
panic!("already terminated!");
34+
let fcx = cx.fcx;
35+
if let Some(span) = fcx.span {
36+
cx.tcx().sess.span_bug(
37+
span,
38+
"already terminated!");
39+
} else {
40+
cx.tcx().sess.bug("already terminated!");
41+
}
3542
}
3643
}
3744

src/librustc_trans/trans/controlflow.rs

+43
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use trans::cleanup::CleanupMethods;
1919
use trans::cleanup;
2020
use trans::common::*;
2121
use trans::consts;
22+
use trans::datum;
2223
use trans::debuginfo;
2324
use trans::debuginfo::{DebugLoc, ToDebugLoc};
2425
use trans::expr;
@@ -144,6 +145,48 @@ pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
144145
return bcx;
145146
}
146147

148+
/// Same as `trans_block` except it returns a `DatumBlock` instead of storing the
149+
/// the result to a destination. This avoids going through a temporary when it's
150+
/// not needed, primarily to ensure that `unsafe { likely(cond) }` and similar patterns
151+
/// work.
152+
pub fn trans_block_datum<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
153+
b: &ast::Block)
154+
-> datum::DatumBlock<'blk, 'tcx, datum::Expr> {
155+
let _icx = push_ctxt("trans_block_datum");
156+
157+
if bcx.unreachable.get() {
158+
let llval = C_nil(bcx.ccx());
159+
let datum = datum::immediate_rvalue(llval, ty::mk_nil(bcx.tcx()));
160+
return datum::DatumBlock {bcx: bcx, datum: datum.to_expr_datum()};
161+
}
162+
163+
let fcx = bcx.fcx;
164+
let mut bcx = bcx;
165+
166+
let cleanup_debug_loc =
167+
debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
168+
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
169+
170+
for s in &b.stmts {
171+
bcx = trans_stmt(bcx, &**s);
172+
}
173+
174+
let datum = match b.expr {
175+
Some(ref e) if !bcx.unreachable.get() => {
176+
unpack_datum!(bcx, expr::trans(bcx, &**e))
177+
}
178+
_ => {
179+
let llval = C_nil(bcx.ccx());
180+
datum::immediate_rvalue(llval, ty::mk_nil(bcx.tcx())).to_expr_datum()
181+
}
182+
};
183+
184+
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
185+
186+
datum::DatumBlock {bcx: bcx, datum: datum}
187+
}
188+
189+
147190
pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
148191
if_id: ast::NodeId,
149192
cond: &ast::Expr,

src/librustc_trans/trans/expr.rs

+31-7
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
579579

580580
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
581581

582+
let ty = expr_ty(bcx, expr);
583+
582584
return match expr_kind(bcx, expr) {
583585
ty::LvalueExpr | ty::RvalueDatumExpr => {
584586
let datum = unpack_datum!(bcx, {
@@ -590,11 +592,10 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
590592

591593
ty::RvalueStmtExpr => {
592594
bcx = trans_rvalue_stmt_unadjusted(bcx, expr);
593-
nil(bcx, expr_ty(bcx, expr))
595+
nil(bcx, ty)
594596
}
595597

596598
ty::RvalueDpsExpr => {
597-
let ty = expr_ty(bcx, expr);
598599
if type_is_zero_size(bcx.ccx(), ty) {
599600
bcx = trans_rvalue_dps_unadjusted(bcx, expr, Ignore);
600601
nil(bcx, ty)
@@ -631,6 +632,7 @@ fn trans_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
631632
// Get the appropriate expression kind for the expression. Most of the time this just uses
632633
// ty::expr_kind, but `ExprCall`s can be treated as `RvalueDatumExpr`s in some cases.
633634
fn expr_kind<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) -> ty::ExprKind {
635+
let ty = expr_ty(bcx, expr);
634636
match expr.node {
635637
ast::ExprCall(ref f, _) if !bcx.tcx().is_method_call(expr.id) => {
636638
if let ast::ExprPath(..) = f.node {
@@ -655,6 +657,21 @@ fn expr_kind<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr) -> ty::ExprKi
655657
}
656658
}
657659
}
660+
ast::ExprBlock(ref b) if !bcx.fcx.type_needs_drop(ty) => {
661+
// Only consider the final expression if it's reachable
662+
let reachable = if let Some(ref cfg) = bcx.fcx.cfg {
663+
cfg.node_is_reachable(expr.id)
664+
} else {
665+
true
666+
};
667+
// Use the kind of the last expression in the block, since
668+
// it's the only one that actually matters
669+
if let Some(ref expr) = b.expr {
670+
if reachable {
671+
return expr_kind(bcx, expr);
672+
}
673+
}
674+
}
658675
_ => ()
659676
}
660677

@@ -757,6 +774,9 @@ fn trans_datum_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
757774
DatumBlock::new(bcx, datum.to_expr_datum())
758775
}
759776
}
777+
ast::ExprBlock(ref b) => {
778+
controlflow::trans_block_datum(bcx, b)
779+
}
760780
_ => {
761781
bcx.tcx().sess.span_bug(
762782
expr.span,
@@ -1079,6 +1099,9 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10791099
ast::ExprInlineAsm(ref a) => {
10801100
asm::trans_inline_asm(bcx, a)
10811101
}
1102+
ast::ExprBlock(ref b) => {
1103+
controlflow::trans_block(bcx, b, Ignore)
1104+
}
10821105
_ => {
10831106
bcx.tcx().sess.span_bug(
10841107
expr.span,
@@ -1097,6 +1120,10 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10971120
let mut bcx = bcx;
10981121
let tcx = bcx.tcx();
10991122

1123+
if bcx.unreachable.get() {
1124+
return bcx;
1125+
}
1126+
11001127
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span);
11011128

11021129
match expr.node {
@@ -2479,13 +2506,10 @@ impl OverflowOpViaIntrinsic {
24792506

24802507
let val = Call(bcx, llfn, &[lhs, rhs], None, binop_debug_loc);
24812508
let result = ExtractValue(bcx, val, 0); // iN operation result
2482-
let overflow = ExtractValue(bcx, val, 1); // i1 "did it overflow?"
2483-
2484-
let cond = ICmp(bcx, llvm::IntEQ, overflow, C_integral(Type::i1(bcx.ccx()), 1, false),
2485-
binop_debug_loc);
2509+
let cond = ExtractValue(bcx, val, 1); // i1 "did it overflow?"
24862510

24872511
let expect = bcx.ccx().get_intrinsic(&"llvm.expect.i1");
2488-
Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
2512+
let cond = Call(bcx, expect, &[cond, C_integral(Type::i1(bcx.ccx()), 0, false)],
24892513
None, binop_debug_loc);
24902514

24912515
let bcx =

src/test/codegen/expect.rs

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// compile-flags: -C no-prepopulate-passes
12+
13+
// The expect intrinsic needs to have the output from the call fed directly
14+
// into the branch, these tests make sure that happens
15+
16+
#![feature(core)]
17+
18+
use std::intrinsics::{likely,unlikely};
19+
20+
// CHECK-LABEL: direct_likely
21+
#[no_mangle]
22+
pub fn direct_likely(x: i32) {
23+
unsafe {
24+
// CHECK: [[VAR:%[0-9]+]] = call i1 @llvm.expect.i1(i1 %{{.*}}, i1 true)
25+
// CHECK: br i1 [[VAR]], label %{{.+}}, label %{{.*}}
26+
if likely(x == 1) {}
27+
}
28+
}
29+
30+
// CHECK-LABEL: direct_unlikely
31+
#[no_mangle]
32+
pub fn direct_unlikely(x: i32) {
33+
unsafe {
34+
// CHECK: [[VAR:%[0-9]+]] = call i1 @llvm.expect.i1(i1 %{{.*}}, i1 false)
35+
// CHECK: br i1 [[VAR]], label %{{.+}}, label %{{.*}}
36+
if unlikely(x == 1) {}
37+
}
38+
}
39+
40+
// CHECK-LABEL: wrapped_likely
41+
// Make sure you can wrap just the call to the intrinsic in `unsafe` and still
42+
// have it work
43+
#[no_mangle]
44+
pub fn wrapped_likely(x: i32) {
45+
// CHECK: [[VAR:%[0-9]+]] = call i1 @llvm.expect.i1(i1 %{{.*}}, i1 true)
46+
// CHECK: br i1 [[VAR]], label %{{.+}}, label %{{.*}}
47+
if unsafe { likely(x == 1) } {}
48+
}
49+
50+
// CHECK-LABEL: wrapped_unlikely
51+
#[no_mangle]
52+
pub fn wrapped_unlikely(x: i32) {
53+
// CHECK: [[VAR:%[0-9]+]] = call i1 @llvm.expect.i1(i1 %{{.*}}, i1 false)
54+
// CHECK: br i1 [[VAR]], label %{{.+}}, label %{{.*}}
55+
if unsafe { unlikely(x == 1) } {}
56+
}

0 commit comments

Comments
 (0)