Skip to content

Commit a55748f

Browse files
committed
Auto merge of #84993 - eddyb:cg-ssa-on-demand-blocks, r=nagisa
rustc_codegen_ssa: only create backend `BasicBlock`s as-needed. Instead of creating one backend (e.g. LLVM) block per MIR block ahead of time, and then deleting the ones that weren't visited, this PR moves to creating the blocks as they're needed (either reached via the RPO visit, or used as the target of a branch from a different block). As deleting a block was the only `unsafe` builder method (generally we only *create* backend objects, not *remove* them), that's gone now and codegen is overall a bit safer. The only change in output is the order of LLVM blocks (which AFAIK has no semantic meaning, other than the first block being the entry block). This happens because the blocks are now created due to control-flow edges, rather than MIR block order. I'm making this a standalone PR because I keep getting wild perf results when I change *anything* in codegen, but if you want to read more about my plans in this area, see #84771 (comment) (and #84771 (comment) - but that may be a bit outdated). (You may notice some of the APIs in this PR, like `append_block`, don't help with the future plans - but I didn't want to include the necessary refactors that pass a build around everywhere, in this PR, so it's a small compromise) r? `@nagisa` `@bjorn3`
2 parents fe72845 + 0fcaf11 commit a55748f

File tree

10 files changed

+100
-85
lines changed

10 files changed

+100
-85
lines changed

compiler/rustc_codegen_llvm/src/builder.rs

+25-25
Original file line numberDiff line numberDiff line change
@@ -118,24 +118,16 @@ macro_rules! builder_methods_for_value_instructions {
118118
}
119119

120120
impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
121-
fn new_block<'b>(cx: &'a CodegenCx<'ll, 'tcx>, llfn: &'ll Value, name: &'b str) -> Self {
122-
let mut bx = Builder::with_cx(cx);
123-
let llbb = unsafe {
124-
let name = SmallCStr::new(name);
125-
llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, name.as_ptr())
126-
};
127-
bx.position_at_end(llbb);
121+
fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Self {
122+
let bx = Builder::with_cx(cx);
123+
unsafe {
124+
llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb);
125+
}
128126
bx
129127
}
130128

131-
fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self {
132-
// Create a fresh builder from the crate context.
133-
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) };
134-
Builder { llbuilder, cx }
135-
}
136-
137-
fn build_sibling_block(&self, name: &str) -> Self {
138-
Builder::new_block(self.cx, self.llfn(), name)
129+
fn cx(&self) -> &CodegenCx<'ll, 'tcx> {
130+
self.cx
139131
}
140132

141133
fn llbb(&self) -> &'ll BasicBlock {
@@ -144,12 +136,22 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
144136

145137
fn set_span(&mut self, _span: Span) {}
146138

147-
fn position_at_end(&mut self, llbb: &'ll BasicBlock) {
139+
fn append_block(cx: &'a CodegenCx<'ll, 'tcx>, llfn: &'ll Value, name: &str) -> &'ll BasicBlock {
148140
unsafe {
149-
llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb);
141+
let name = SmallCStr::new(name);
142+
llvm::LLVMAppendBasicBlockInContext(cx.llcx, llfn, name.as_ptr())
150143
}
151144
}
152145

146+
fn append_sibling_block(&mut self, name: &str) -> &'ll BasicBlock {
147+
Self::append_block(self.cx, self.llfn(), name)
148+
}
149+
150+
fn build_sibling_block(&mut self, name: &str) -> Self {
151+
let llbb = self.append_sibling_block(name);
152+
Self::build(self.cx, llbb)
153+
}
154+
153155
fn ret_void(&mut self) {
154156
unsafe {
155157
llvm::LLVMBuildRetVoid(self.llbuilder);
@@ -1144,14 +1146,6 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
11441146
unsafe { llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty, UNNAMED) }
11451147
}
11461148

1147-
fn cx(&self) -> &CodegenCx<'ll, 'tcx> {
1148-
self.cx
1149-
}
1150-
1151-
unsafe fn delete_basic_block(&mut self, bb: &'ll BasicBlock) {
1152-
llvm::LLVMDeleteBasicBlock(bb);
1153-
}
1154-
11551149
fn do_not_inline(&mut self, llret: &'ll Value) {
11561150
llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret);
11571151
}
@@ -1165,6 +1159,12 @@ impl StaticBuilderMethods for Builder<'a, 'll, 'tcx> {
11651159
}
11661160

11671161
impl Builder<'a, 'll, 'tcx> {
1162+
fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self {
1163+
// Create a fresh builder from the crate context.
1164+
let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) };
1165+
Builder { llbuilder, cx }
1166+
}
1167+
11681168
pub fn llfn(&self) -> &'ll Value {
11691169
unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) }
11701170
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ fn declare_unused_fn(cx: &CodegenCx<'ll, 'tcx>, def_id: &DefId) -> Instance<'tcx
223223

224224
fn codegen_unused_fn_and_counter(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>) {
225225
let llfn = cx.get_fn(instance);
226-
let mut bx = Builder::new_block(cx, llfn, "unused_function");
226+
let llbb = Builder::append_block(cx, llfn, "unused_function");
227+
let mut bx = Builder::build(cx, llbb);
227228
let fn_name = bx.get_pgo_func_name_var(instance);
228229
let hash = bx.const_u64(0);
229230
let num_counters = bx.const_u32(1);

compiler/rustc_codegen_llvm/src/intrinsic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,8 @@ fn gen_fn<'ll, 'tcx>(
678678
cx.apply_target_cpu_attr(llfn);
679679
// FIXME(eddyb) find a nicer way to do this.
680680
unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) };
681-
let bx = Builder::new_block(cx, llfn, "entry-block");
681+
let llbb = Builder::append_block(cx, llfn, "entry-block");
682+
let bx = Builder::build(cx, llbb);
682683
codegen(bx);
683684
llfn
684685
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,6 @@ extern "C" {
10791079
Fn: &'a Value,
10801080
Name: *const c_char,
10811081
) -> &'a BasicBlock;
1082-
pub fn LLVMDeleteBasicBlock(BB: &BasicBlock);
10831082

10841083
// Operations on instructions
10851084
pub fn LLVMIsAInstruction(Val: &Value) -> Option<&Value>;

compiler/rustc_codegen_ssa/src/base.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
409409
cx.set_frame_pointer_elimination(llfn);
410410
cx.apply_target_cpu_attr(llfn);
411411

412-
let mut bx = Bx::new_block(&cx, llfn, "top");
412+
let llbb = Bx::append_block(&cx, llfn, "top");
413+
let mut bx = Bx::build(&cx, llbb);
413414

414415
bx.insert_reference_to_gdb_debug_scripts_section_global();
415416

compiler/rustc_codegen_ssa/src/mir/block.rs

+27-12
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
6868
target: mir::BasicBlock,
6969
) -> (Bx::BasicBlock, bool) {
7070
let span = self.terminator.source_info.span;
71-
let lltarget = fx.blocks[target];
71+
let lltarget = fx.llbb(target);
7272
let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
7373
match (self.funclet_bb, target_funclet) {
7474
(None, None) => (lltarget, false),
@@ -133,13 +133,13 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
133133
// If there is a cleanup block and the function we're calling can unwind, then
134134
// do an invoke, otherwise do a call.
135135
if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
136-
let ret_bx = if let Some((_, target)) = destination {
137-
fx.blocks[target]
136+
let ret_llbb = if let Some((_, target)) = destination {
137+
fx.llbb(target)
138138
} else {
139139
fx.unreachable_block()
140140
};
141141
let invokeret =
142-
bx.invoke(fn_ptr, &llargs, ret_bx, self.llblock(fx, cleanup), self.funclet(fx));
142+
bx.invoke(fn_ptr, &llargs, ret_llbb, self.llblock(fx, cleanup), self.funclet(fx));
143143
bx.apply_attrs_callsite(&fn_abi, invokeret);
144144

145145
if let Some((ret_dest, target)) = destination {
@@ -386,7 +386,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
386386

387387
// Create the failure block and the conditional branch to it.
388388
let lltarget = helper.llblock(self, target);
389-
let panic_block = self.new_block("panic");
389+
let panic_block = bx.build_sibling_block("panic");
390390
if expected {
391391
bx.cond_br(cond, lltarget, panic_block.llbb());
392392
} else {
@@ -1205,7 +1205,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12051205

12061206
// FIXME(eddyb) rename this to `eh_pad_for_uncached`.
12071207
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
1208-
let llbb = self.blocks[bb];
1208+
let llbb = self.llbb(bb);
12091209
if base::wants_msvc_seh(self.cx.sess()) {
12101210
let funclet;
12111211
let ret_llbb;
@@ -1289,14 +1289,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12891289
})
12901290
}
12911291

1292-
pub fn new_block(&self, name: &str) -> Bx {
1293-
Bx::new_block(self.cx, self.llfn, name)
1292+
// FIXME(eddyb) replace with `build_sibling_block`/`append_sibling_block`
1293+
// (which requires having a `Bx` already, and not all callers do).
1294+
fn new_block(&self, name: &str) -> Bx {
1295+
let llbb = Bx::append_block(self.cx, self.llfn, name);
1296+
Bx::build(self.cx, llbb)
12941297
}
12951298

1296-
pub fn build_block(&self, bb: mir::BasicBlock) -> Bx {
1297-
let mut bx = Bx::with_cx(self.cx);
1298-
bx.position_at_end(self.blocks[bb]);
1299-
bx
1299+
/// Get the backend `BasicBlock` for a MIR `BasicBlock`, either already
1300+
/// cached in `self.cached_llbbs`, or created on demand (and cached).
1301+
// FIXME(eddyb) rename `llbb` and other `ll`-prefixed things to use a
1302+
// more backend-agnostic prefix such as `cg` (i.e. this would be `cgbb`).
1303+
pub fn llbb(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
1304+
self.cached_llbbs[bb].unwrap_or_else(|| {
1305+
// FIXME(eddyb) only name the block if `fewer_names` is `false`.
1306+
let llbb = Bx::append_block(self.cx, self.llfn, &format!("{:?}", bb));
1307+
self.cached_llbbs[bb] = Some(llbb);
1308+
llbb
1309+
})
1310+
}
1311+
1312+
pub fn build_block(&mut self, bb: mir::BasicBlock) -> Bx {
1313+
let llbb = self.llbb(bb);
1314+
Bx::build(self.cx, llbb)
13001315
}
13011316

13021317
fn make_return_dest(

compiler/rustc_codegen_ssa/src/mir/mod.rs

+22-33
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
4040
/// then later loaded when generating the DIVERGE_BLOCK.
4141
personality_slot: Option<PlaceRef<'tcx, Bx::Value>>,
4242

43-
/// A `Block` for each MIR `BasicBlock`
44-
blocks: IndexVec<mir::BasicBlock, Bx::BasicBlock>,
43+
/// A backend `BasicBlock` for each MIR `BasicBlock`, created lazily
44+
/// as-needed (e.g. RPO reaching it or another block branching to it).
45+
// FIXME(eddyb) rename `llbbs` and other `ll`-prefixed things to use a
46+
// more backend-agnostic prefix such as `cg` (i.e. this would be `cgbbs`).
47+
cached_llbbs: IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>>,
4548

4649
/// The funclet status of each basic block
4750
cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
@@ -141,7 +144,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
141144

142145
let debug_context = cx.create_function_debug_context(instance, &fn_abi, llfn, &mir);
143146

144-
let mut bx = Bx::new_block(cx, llfn, "start");
147+
let start_llbb = Bx::append_block(cx, llfn, "start");
148+
let mut bx = Bx::build(cx, start_llbb);
145149

146150
if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) {
147151
bx.set_personality_fn(cx.eh_personality());
@@ -151,17 +155,17 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
151155
// Allocate a `Block` for every basic block, except
152156
// the start block, if nothing loops back to it.
153157
let reentrant_start_block = !mir.predecessors()[mir::START_BLOCK].is_empty();
154-
let block_bxs: IndexVec<mir::BasicBlock, Bx::BasicBlock> = mir
155-
.basic_blocks()
156-
.indices()
157-
.map(|bb| {
158-
if bb == mir::START_BLOCK && !reentrant_start_block {
159-
bx.llbb()
160-
} else {
161-
bx.build_sibling_block(&format!("{:?}", bb)).llbb()
162-
}
163-
})
164-
.collect();
158+
let cached_llbbs: IndexVec<mir::BasicBlock, Option<Bx::BasicBlock>> =
159+
mir.basic_blocks()
160+
.indices()
161+
.map(|bb| {
162+
if bb == mir::START_BLOCK && !reentrant_start_block {
163+
Some(start_llbb)
164+
} else {
165+
None
166+
}
167+
})
168+
.collect();
165169

166170
let mut fx = FunctionCx {
167171
instance,
@@ -170,7 +174,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
170174
fn_abi,
171175
cx,
172176
personality_slot: None,
173-
blocks: block_bxs,
177+
cached_llbbs,
174178
unreachable_block: None,
175179
cleanup_kinds,
176180
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
@@ -245,29 +249,14 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
245249

246250
// Branch to the START block, if it's not the entry block.
247251
if reentrant_start_block {
248-
bx.br(fx.blocks[mir::START_BLOCK]);
252+
bx.br(fx.llbb(mir::START_BLOCK));
249253
}
250254

251-
let rpo = traversal::reverse_postorder(&mir);
252-
let mut visited = BitSet::new_empty(mir.basic_blocks().len());
253-
254255
// Codegen the body of each block using reverse postorder
255-
for (bb, _) in rpo {
256-
visited.insert(bb.index());
256+
// FIXME(eddyb) reuse RPO iterator between `analysis` and this.
257+
for (bb, _) in traversal::reverse_postorder(&mir) {
257258
fx.codegen_block(bb);
258259
}
259-
260-
// Remove blocks that haven't been visited, or have no
261-
// predecessors.
262-
for bb in mir.basic_blocks().indices() {
263-
// Unreachable block
264-
if !visited.contains(bb.index()) {
265-
debug!("codegen_mir: block {:?} was not visited", bb);
266-
unsafe {
267-
bx.delete_basic_block(fx.blocks[bb]);
268-
}
269-
}
270-
}
271260
}
272261

273262
/// Produces, for each argument, a `Value` pointing at the

compiler/rustc_codegen_ssa/src/traits/builder.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,21 @@ pub trait BuilderMethods<'a, 'tcx>:
4040
+ HasParamEnv<'tcx>
4141
+ HasTargetSpec
4242
{
43-
fn new_block<'b>(cx: &'a Self::CodegenCx, llfn: Self::Function, name: &'b str) -> Self;
44-
fn with_cx(cx: &'a Self::CodegenCx) -> Self;
45-
fn build_sibling_block(&self, name: &str) -> Self;
43+
fn build(cx: &'a Self::CodegenCx, llbb: Self::BasicBlock) -> Self;
44+
4645
fn cx(&self) -> &Self::CodegenCx;
4746
fn llbb(&self) -> Self::BasicBlock;
47+
4848
fn set_span(&mut self, span: Span);
4949

50-
fn position_at_end(&mut self, llbb: Self::BasicBlock);
50+
// FIXME(eddyb) replace uses of this with `append_sibling_block`.
51+
fn append_block(cx: &'a Self::CodegenCx, llfn: Self::Function, name: &str) -> Self::BasicBlock;
52+
53+
fn append_sibling_block(&mut self, name: &str) -> Self::BasicBlock;
54+
55+
// FIXME(eddyb) replace with callers using `append_sibling_block`.
56+
fn build_sibling_block(&mut self, name: &str) -> Self;
57+
5158
fn ret_void(&mut self);
5259
fn ret(&mut self, v: Self::Value);
5360
fn br(&mut self, dest: Self::BasicBlock);
@@ -291,6 +298,5 @@ pub trait BuilderMethods<'a, 'tcx>:
291298
) -> Self::Value;
292299
fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value;
293300

294-
unsafe fn delete_basic_block(&mut self, bb: Self::BasicBlock);
295301
fn do_not_inline(&mut self, llret: Self::Value);
296302
}

src/test/codegen/drop.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,16 @@ pub fn droppy() {
2323
// FIXME(eddyb) the `void @` forces a match on the instruction, instead of the
2424
// comment, that's `; call core::ptr::drop_in_place::<drop::SomeUniqueName>`
2525
// for the `v0` mangling, should switch to matching on that once `legacy` is gone.
26-
// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
27-
// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
28-
// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
2926
// CHECK-NOT: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
3027
// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
3128
// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
29+
// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
30+
// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
31+
// CHECK-NOT: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
3232
// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
33+
// CHECK-NOT: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
34+
// CHECK: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
35+
// CHECK-NOT: invoke void @{{.*}}drop_in_place{{.*}}SomeUniqueName
3336
// CHECK: call void @{{.*}}drop_in_place{{.*}}SomeUniqueName
3437
// CHECK-NOT: {{(call|invoke) void @.*}}drop_in_place{{.*}}SomeUniqueName
3538
// The next line checks for the } that ends the function definition

src/test/codegen/match.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ pub fn exhaustive_match(e: E) -> u8 {
1414
// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[A:[a-zA-Z0-9_]+]]
1515
// CHECK-NEXT: i[[TY:[0-9]+]] [[DISCR:[0-9]+]], label %[[B:[a-zA-Z0-9_]+]]
1616
// CHECK-NEXT: ]
17-
// CHECK: [[B]]:
18-
// CHECK-NEXT: store i8 1, i8* %1, align 1
19-
// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]]
2017
// CHECK: [[OTHERWISE]]:
2118
// CHECK-NEXT: unreachable
2219
// CHECK: [[A]]:
2320
// CHECK-NEXT: store i8 0, i8* %1, align 1
21+
// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]]
22+
// CHECK: [[B]]:
23+
// CHECK-NEXT: store i8 1, i8* %1, align 1
2424
// CHECK-NEXT: br label %[[EXIT:[a-zA-Z0-9_]+]]
2525
match e {
2626
E::A => 0,

0 commit comments

Comments
 (0)