Skip to content

Commit 43ef63d

Browse files
committed
Auto merge of #40367 - eddyb:naked-cruft, r=nagisa
Improve the LLVM IR we generate for trivial functions, especially #[naked] ones. These two small changes fix edef1c/libfringe#68: * Don't emit ZST allocas, such as when returning `()` * Don't emit a branch from LLVM's entry block to MIR's `START_BLOCK` unless needed * That is, if a loop branches back to it, although I'm not sure that's even valid MIR
2 parents 1f59c7e + 9b5c577 commit 43ef63d

File tree

6 files changed

+98
-59
lines changed

6 files changed

+98
-59
lines changed

src/librustc_trans/debuginfo/metadata.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,8 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext,
784784
};
785785

786786
debug!("compile_unit_metadata: {:?}", compile_unit_name);
787-
let producer = format!("rustc version {}",
787+
// FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice.
788+
let producer = format!("clang LLVM (rustc version {})",
788789
(option_env!("CFG_VERSION")).expect("CFG_VERSION"));
789790

790791
let compile_unit_name = compile_unit_name.as_ptr();

src/librustc_trans/mir/analyze.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use rustc::mir::visit::{Visitor, LvalueContext};
1919
use rustc::mir::traversal;
2020
use common;
2121
use super::MirContext;
22-
use super::rvalue;
2322

2423
pub fn lvalue_locals<'a, 'tcx>(mircx: &MirContext<'a, 'tcx>) -> BitVector {
2524
let mir = mircx.mir;
@@ -93,7 +92,7 @@ impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> {
9392

9493
if let mir::Lvalue::Local(index) = *lvalue {
9594
self.mark_assigned(index);
96-
if !rvalue::rvalue_creates_operand(rvalue) {
95+
if !self.cx.rvalue_creates_operand(rvalue) {
9796
self.mark_as_lvalue(index);
9897
}
9998
} else {

src/librustc_trans/mir/mod.rs

+14-23
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,16 @@
1111
use libc::c_uint;
1212
use llvm::{self, ValueRef, BasicBlockRef};
1313
use llvm::debuginfo::DIScope;
14-
use rustc::ty;
14+
use rustc::ty::{self, Ty, TypeFoldable};
1515
use rustc::ty::layout::{self, LayoutTyper};
1616
use rustc::mir::{self, Mir};
1717
use rustc::mir::tcx::LvalueTy;
1818
use rustc::ty::subst::Substs;
1919
use rustc::infer::TransNormalize;
20-
use rustc::ty::TypeFoldable;
2120
use session::config::FullDebugInfo;
2221
use base;
2322
use builder::Builder;
24-
use common::{self, CrateContext, C_null, Funclet};
23+
use common::{self, CrateContext, Funclet};
2524
use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext};
2625
use monomorphize::{self, Instance};
2726
use abi::FnType;
@@ -171,23 +170,12 @@ enum LocalRef<'tcx> {
171170

172171
impl<'tcx> LocalRef<'tcx> {
173172
fn new_operand<'a>(ccx: &CrateContext<'a, 'tcx>,
174-
ty: ty::Ty<'tcx>) -> LocalRef<'tcx> {
173+
ty: Ty<'tcx>) -> LocalRef<'tcx> {
175174
if common::type_is_zero_size(ccx, ty) {
176175
// Zero-size temporaries aren't always initialized, which
177176
// doesn't matter because they don't contain data, but
178177
// we need something in the operand.
179-
let llty = type_of::type_of(ccx, ty);
180-
let val = if common::type_is_imm_pair(ccx, ty) {
181-
let fields = llty.field_types();
182-
OperandValue::Pair(C_null(fields[0]), C_null(fields[1]))
183-
} else {
184-
OperandValue::Immediate(C_null(llty))
185-
};
186-
let op = OperandRef {
187-
val: val,
188-
ty: ty
189-
};
190-
LocalRef::Operand(Some(op))
178+
LocalRef::Operand(Some(OperandRef::new_zst(ccx, ty)))
191179
} else {
192180
LocalRef::Operand(None)
193181
}
@@ -207,15 +195,17 @@ pub fn trans_mir<'a, 'tcx: 'a>(
207195
debug!("fn_ty: {:?}", fn_ty);
208196
let debug_context =
209197
debuginfo::create_function_debug_context(ccx, instance, sig, llfn, mir);
210-
let bcx = Builder::new_block(ccx, llfn, "entry-block");
198+
let bcx = Builder::new_block(ccx, llfn, "start");
211199

212200
let cleanup_kinds = analyze::cleanup_kinds(&mir);
213201

214-
// Allocate a `Block` for every basic block
202+
// Allocate a `Block` for every basic block, except
203+
// the start block, if nothing loops back to it.
204+
let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty();
215205
let block_bcxs: IndexVec<mir::BasicBlock, BasicBlockRef> =
216206
mir.basic_blocks().indices().map(|bb| {
217-
if bb == mir::START_BLOCK {
218-
bcx.build_sibling_block("start").llbb()
207+
if bb == mir::START_BLOCK && !reentrant_start_block {
208+
bcx.llbb()
219209
} else {
220210
bcx.build_sibling_block(&format!("{:?}", bb)).llbb()
221211
}
@@ -301,9 +291,10 @@ pub fn trans_mir<'a, 'tcx: 'a>(
301291
.collect()
302292
};
303293

304-
// Branch to the START block
305-
let start_bcx = mircx.blocks[mir::START_BLOCK];
306-
bcx.br(start_bcx);
294+
// Branch to the START block, if it's not the entry block.
295+
if reentrant_start_block {
296+
bcx.br(mircx.blocks[mir::START_BLOCK]);
297+
}
307298

308299
// Up until here, IR instructions for this function have explicitly not been annotated with
309300
// source code location, so we don't step into call setup code. From here on, source location

src/librustc_trans/mir/operand.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc::mir::tcx::LvalueTy;
1616
use rustc_data_structures::indexed_vec::Idx;
1717

1818
use base;
19-
use common;
19+
use common::{self, CrateContext, C_null};
2020
use builder::Builder;
2121
use value::Value;
2222
use type_of;
@@ -79,6 +79,22 @@ impl<'tcx> fmt::Debug for OperandRef<'tcx> {
7979
}
8080

8181
impl<'a, 'tcx> OperandRef<'tcx> {
82+
pub fn new_zst(ccx: &CrateContext<'a, 'tcx>,
83+
ty: Ty<'tcx>) -> OperandRef<'tcx> {
84+
assert!(common::type_is_zero_size(ccx, ty));
85+
let llty = type_of::type_of(ccx, ty);
86+
let val = if common::type_is_imm_pair(ccx, ty) {
87+
let fields = llty.field_types();
88+
OperandValue::Pair(C_null(fields[0]), C_null(fields[1]))
89+
} else {
90+
OperandValue::Immediate(C_null(llty))
91+
};
92+
OperandRef {
93+
val: val,
94+
ty: ty
95+
}
96+
}
97+
8298
/// Asserts that this operand refers to a scalar and returns
8399
/// a reference to its value.
84100
pub fn immediate(self) -> ValueRef {

src/librustc_trans/mir/rvalue.rs

+27-22
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
158158
}
159159

160160
_ => {
161-
assert!(rvalue_creates_operand(rvalue));
161+
assert!(self.rvalue_creates_operand(rvalue));
162162
let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
163163
self.store_operand(&bcx, dest.llval, dest.alignment.to_align(), temp);
164164
bcx
@@ -171,7 +171,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
171171
rvalue: &mir::Rvalue<'tcx>)
172172
-> (Builder<'a, 'tcx>, OperandRef<'tcx>)
173173
{
174-
assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
174+
assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
175175

176176
match *rvalue {
177177
mir::Rvalue::Cast(ref kind, ref source, cast_ty) => {
@@ -466,8 +466,10 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
466466
}
467467
mir::Rvalue::Repeat(..) |
468468
mir::Rvalue::Aggregate(..) => {
469-
bug!("cannot generate operand from rvalue {:?}", rvalue);
470-
469+
// According to `rvalue_creates_operand`, only ZST
470+
// aggregate rvalues are allowed to be operands.
471+
let ty = rvalue.ty(self.mir, self.ccx.tcx());
472+
(bcx, OperandRef::new_zst(self.ccx, self.monomorphize(&ty)))
471473
}
472474
}
473475
}
@@ -650,26 +652,29 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
650652

651653
OperandValue::Pair(val, of)
652654
}
653-
}
654655

655-
pub fn rvalue_creates_operand(rvalue: &mir::Rvalue) -> bool {
656-
match *rvalue {
657-
mir::Rvalue::Ref(..) |
658-
mir::Rvalue::Len(..) |
659-
mir::Rvalue::Cast(..) | // (*)
660-
mir::Rvalue::BinaryOp(..) |
661-
mir::Rvalue::CheckedBinaryOp(..) |
662-
mir::Rvalue::UnaryOp(..) |
663-
mir::Rvalue::Discriminant(..) |
664-
mir::Rvalue::Box(..) |
665-
mir::Rvalue::Use(..) => // (*)
666-
true,
667-
mir::Rvalue::Repeat(..) |
668-
mir::Rvalue::Aggregate(..) =>
669-
false,
670-
}
656+
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool {
657+
match *rvalue {
658+
mir::Rvalue::Ref(..) |
659+
mir::Rvalue::Len(..) |
660+
mir::Rvalue::Cast(..) | // (*)
661+
mir::Rvalue::BinaryOp(..) |
662+
mir::Rvalue::CheckedBinaryOp(..) |
663+
mir::Rvalue::UnaryOp(..) |
664+
mir::Rvalue::Discriminant(..) |
665+
mir::Rvalue::Box(..) |
666+
mir::Rvalue::Use(..) => // (*)
667+
true,
668+
mir::Rvalue::Repeat(..) |
669+
mir::Rvalue::Aggregate(..) => {
670+
let ty = rvalue.ty(self.mir, self.ccx.tcx());
671+
let ty = self.monomorphize(&ty);
672+
common::type_is_zero_size(self.ccx, ty)
673+
}
674+
}
671675

672-
// (*) this is only true if the type is suitable
676+
// (*) this is only true if the type is suitable
677+
}
673678
}
674679

675680
#[derive(Copy, Clone)]

src/test/codegen/naked-functions.rs

+37-10
Original file line numberDiff line numberDiff line change
@@ -20,25 +20,28 @@
2020
#[no_mangle]
2121
#[naked]
2222
fn naked_empty() {
23-
// CHECK: ret void
23+
// CHECK-NEXT: {{.+}}:
24+
// CHECK-NEXT: ret void
2425
}
2526

2627
// CHECK: Function Attrs: naked uwtable
2728
#[no_mangle]
2829
#[naked]
2930
// CHECK-NEXT: define internal void @naked_with_args(i{{[0-9]+}})
3031
fn naked_with_args(a: isize) {
31-
// CHECK: %a = alloca i{{[0-9]+}}
32-
// CHECK: ret void
32+
// CHECK-NEXT: {{.+}}:
33+
// CHECK-NEXT: %a = alloca i{{[0-9]+}}
3334
&a; // keep variable in an alloca
35+
// CHECK: ret void
3436
}
3537

3638
// CHECK: Function Attrs: naked uwtable
3739
// CHECK-NEXT: define internal i{{[0-9]+}} @naked_with_return()
3840
#[no_mangle]
3941
#[naked]
4042
fn naked_with_return() -> isize {
41-
// CHECK: ret i{{[0-9]+}} 0
43+
// CHECK-NEXT: {{.+}}:
44+
// CHECK-NEXT: ret i{{[0-9]+}} 0
4245
0
4346
}
4447

@@ -47,9 +50,10 @@ fn naked_with_return() -> isize {
4750
#[no_mangle]
4851
#[naked]
4952
fn naked_with_args_and_return(a: isize) -> isize {
50-
// CHECK: %a = alloca i{{[0-9]+}}
51-
// CHECK: ret i{{[0-9]+}} %{{[0-9]+}}
53+
// CHECK-NEXT: {{.+}}:
54+
// CHECK-NEXT: %a = alloca i{{[0-9]+}}
5255
&a; // keep variable in an alloca
56+
// CHECK: ret i{{[0-9]+}} %{{[0-9]+}}
5357
a
5458
}
5559

@@ -58,14 +62,37 @@ fn naked_with_args_and_return(a: isize) -> isize {
5862
#[no_mangle]
5963
#[naked]
6064
fn naked_recursive() {
61-
// CHECK: call void @naked_empty()
65+
// CHECK-NEXT: {{.+}}:
66+
// CHECK-NEXT: call void @naked_empty()
67+
68+
// FIXME(#39685) Avoid one block per call.
69+
// CHECK-NEXT: br label %bb1
70+
// CHECK: bb1:
71+
6272
naked_empty();
63-
// CHECK: %{{[0-9]+}} = call i{{[0-9]+}} @naked_with_return()
73+
74+
// CHECK-NEXT: %{{[0-9]+}} = call i{{[0-9]+}} @naked_with_return()
75+
76+
// FIXME(#39685) Avoid one block per call.
77+
// CHECK-NEXT: br label %bb2
78+
// CHECK: bb2:
79+
80+
// CHECK-NEXT: %{{[0-9]+}} = call i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}} %{{[0-9]+}})
81+
82+
// FIXME(#39685) Avoid one block per call.
83+
// CHECK-NEXT: br label %bb3
84+
// CHECK: bb3:
85+
86+
// CHECK-NEXT: call void @naked_with_args(i{{[0-9]+}} %{{[0-9]+}})
87+
88+
// FIXME(#39685) Avoid one block per call.
89+
// CHECK-NEXT: br label %bb4
90+
// CHECK: bb4:
91+
6492
naked_with_args(
65-
// CHECK: %{{[0-9]+}} = call i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}} %{{[0-9]+}})
6693
naked_with_args_and_return(
67-
// CHECK: call void @naked_with_args(i{{[0-9]+}} %{{[0-9]+}})
6894
naked_with_return()
6995
)
7096
);
97+
// CHECK-NEXT: ret void
7198
}

0 commit comments

Comments
 (0)