Skip to content

Commit 6187e11

Browse files
committed
Improve handling of immediate values
The primary improvement here is to handle function calls that return imemdiate values such that it doesn't force the use of a stack slot. It also avoids loads from slots that have an store to it in the same block. This especially improves the codegen for intrinsics, as most of them produce immediate values.
1 parent febdc3b commit 6187e11

File tree

10 files changed

+900
-590
lines changed

10 files changed

+900
-590
lines changed

src/librustc_llvm/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,7 @@ extern {
15591559
/* Selected entries from the downcasts. */
15601560
pub fn LLVMIsATerminatorInst(Inst: ValueRef) -> ValueRef;
15611561
pub fn LLVMIsAStoreInst(Inst: ValueRef) -> ValueRef;
1562+
pub fn LLVMIsAZExtInst(Inst: ValueRef) -> ValueRef;
15621563

15631564
/// Writes a module to the specified path. Returns 0 on success.
15641565
pub fn LLVMWriteBitcodeToFile(M: ModuleRef, Path: *const c_char) -> c_int;

src/librustc_trans/trans/base.rs

+104-19
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use trans::callee;
5454
use trans::cleanup::{self, CleanupMethods, DropHint};
5555
use trans::closure;
5656
use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral};
57-
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef};
57+
use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef, C_nil};
5858
use trans::common::{CrateContext, DropFlagHintsMap, Field, FunctionContext};
5959
use trans::common::{Result, NodeIdAndSpan, VariantInfo};
6060
use trans::common::{node_id_type, return_type_is_void};
@@ -699,8 +699,22 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
699699
debug_loc: DebugLoc)
700700
-> (ValueRef, Block<'blk, 'tcx>) {
701701
let _icx = push_ctxt("invoke_");
702+
703+
let ret_ty = match fn_ty.sty {
704+
ty::TyBareFn(_, ref f) => {
705+
let output = bcx.tcx().erase_late_bound_regions(&f.sig.output());
706+
output
707+
}
708+
_ => panic!("expected bare rust fn or closure in trans_call_inner")
709+
};
710+
702711
if bcx.unreachable.get() {
703-
return (C_null(Type::i8(bcx.ccx())), bcx);
712+
if let ty::FnConverging(ty) = ret_ty {
713+
let llty = type_of::arg_type_of(bcx.ccx(), ty);
714+
return (C_undef(llty), bcx);
715+
} else {
716+
return (C_nil(bcx.ccx()), bcx);
717+
}
704718
}
705719

706720
let attributes = attributes::from_fn_type(bcx.ccx(), fn_ty);
@@ -722,25 +736,39 @@ pub fn invoke<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
722736
let normal_bcx = bcx.fcx.new_temp_block("normal-return");
723737
let landing_pad = bcx.fcx.get_landing_pad();
724738

725-
let llresult = Invoke(bcx,
739+
let mut llresult = Invoke(bcx,
726740
llfn,
727741
&llargs[..],
728742
normal_bcx.llbb,
729743
landing_pad,
730744
Some(attributes),
731745
debug_loc);
746+
if let ty::FnConverging(ty) = ret_ty {
747+
if return_type_is_void(bcx.ccx(), ty) {
748+
llresult = C_nil(bcx.ccx());
749+
}
750+
} else {
751+
llresult = C_nil(bcx.ccx());
752+
}
732753
return (llresult, normal_bcx);
733754
} else {
734755
debug!("calling {} at {:?}", bcx.val_to_string(llfn), bcx.llbb);
735756
for &llarg in llargs {
736757
debug!("arg: {}", bcx.val_to_string(llarg));
737758
}
738759

739-
let llresult = Call(bcx,
740-
llfn,
741-
&llargs[..],
742-
Some(attributes),
743-
debug_loc);
760+
let mut llresult = Call(bcx,
761+
llfn,
762+
&llargs[..],
763+
Some(attributes),
764+
debug_loc);
765+
if let ty::FnConverging(ty) = ret_ty {
766+
if return_type_is_void(bcx.ccx(), ty) {
767+
llresult = C_nil(bcx.ccx());
768+
}
769+
} else {
770+
llresult = C_nil(bcx.ccx());
771+
}
744772
return (llresult, bcx);
745773
}
746774
}
@@ -775,23 +803,41 @@ pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
775803
/// gives us better information about what we are loading.
776804
pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
777805
ptr: ValueRef, t: Ty<'tcx>) -> ValueRef {
806+
use trans::basic_block::BasicBlock;
807+
778808
if cx.unreachable.get() || type_is_zero_size(cx.ccx(), t) {
779809
return C_undef(type_of::type_of(cx.ccx(), t));
780810
}
781811

782812
let ptr = to_arg_ty_ptr(cx, ptr, t);
813+
let ptr = Value(ptr);
783814
let align = type_of::align_of(cx.ccx(), t);
784815

816+
let bb = BasicBlock(cx.llbb);
817+
785818
if type_is_immediate(cx.ccx(), t) && type_of::type_of(cx.ccx(), t).is_aggregate() {
786-
let load = Load(cx, ptr);
819+
if let Some(val) = ptr.get_dominating_store(cx) {
820+
let valbb = val.get_parent();
821+
822+
if Some(bb) == valbb {
823+
if let Some(val) = val.get_operand(0) {
824+
debug!("Eliding load from {}", cx.ccx().tn().val_to_string(ptr.get()));
825+
debug!(" Using previous stored value: {}",
826+
cx.ccx().tn().val_to_string(val.get()));
827+
return val.get();
828+
}
829+
}
830+
}
831+
832+
let load = Load(cx, ptr.get());
787833
unsafe {
788834
llvm::LLVMSetAlignment(load, align);
789835
}
790836
return load;
791837
}
792838

793839
unsafe {
794-
let global = llvm::LLVMIsAGlobalVariable(ptr);
840+
let global = llvm::LLVMIsAGlobalVariable(ptr.get());
795841
if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True {
796842
let val = llvm::LLVMGetInitializer(global);
797843
if !val.is_null() {
@@ -800,17 +846,30 @@ pub fn load_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
800846
}
801847
}
802848

849+
if let Some(val) = ptr.get_dominating_store(cx) {
850+
let valbb = val.get_parent();
851+
852+
if Some(bb) == valbb {
853+
if let Some(val) = val.get_operand(0) {
854+
debug!("Eliding load from {}", cx.ccx().tn().val_to_string(ptr.get()));
855+
debug!(" Using previous stored value: {}",
856+
cx.ccx().tn().val_to_string(val.get()));
857+
return to_arg_ty(cx, val.get(), t);
858+
}
859+
}
860+
}
861+
803862
let val = if t.is_bool() {
804-
LoadRangeAssert(cx, ptr, 0, 2, llvm::False)
863+
LoadRangeAssert(cx, ptr.get(), 0, 2, llvm::False)
805864
} else if t.is_char() {
806865
// a char is a Unicode codepoint, and so takes values from 0
807866
// to 0x10FFFF inclusive only.
808-
LoadRangeAssert(cx, ptr, 0, 0x10FFFF + 1, llvm::False)
867+
LoadRangeAssert(cx, ptr.get(), 0, 0x10FFFF + 1, llvm::False)
809868
} else if (t.is_region_ptr() || t.is_unique())
810869
&& !common::type_is_fat_ptr(cx.tcx(), t) {
811-
LoadNonNull(cx, ptr)
870+
LoadNonNull(cx, ptr.get())
812871
} else {
813-
Load(cx, ptr)
872+
Load(cx, ptr.get())
814873
};
815874

816875
unsafe {
@@ -831,7 +890,18 @@ pub fn store_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, dst: ValueRef, t
831890
Store(cx, ExtractValue(cx, v, abi::FAT_PTR_ADDR), expr::get_dataptr(cx, dst));
832891
Store(cx, ExtractValue(cx, v, abi::FAT_PTR_EXTRA), expr::get_len(cx, dst));
833892
} else {
834-
let store = Store(cx, from_arg_ty(cx, v, t), to_arg_ty_ptr(cx, dst, t));
893+
let vty = val_ty(v);
894+
let dstty = val_ty(dst).element_type();
895+
896+
// If the source and destination have the same type, then don't try any conversion,
897+
// this can happen with the return values of functions, as they don't touch an alloca
898+
let (v, dst) = if vty == dstty {
899+
(v, dst)
900+
} else {
901+
(from_arg_ty(cx, v, t), to_arg_ty_ptr(cx, dst, t))
902+
};
903+
904+
let store = Store(cx, v, dst);
835905
unsafe {
836906
llvm::LLVMSetAlignment(store, type_of::align_of(cx.ccx(), t));
837907
}
@@ -848,7 +918,16 @@ pub fn from_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
848918

849919
pub fn to_arg_ty(bcx: Block, val: ValueRef, ty: Ty) -> ValueRef {
850920
if ty.is_bool() {
851-
Trunc(bcx, val, Type::i1(bcx.ccx()))
921+
let val = Value(val);
922+
if let Some(zext) = val.as_zext_inst() {
923+
if let Some(val) = zext.get_operand(0) {
924+
let valty = val_ty(val.get());
925+
if valty == Type::i1(bcx.ccx()) {
926+
return val.get();
927+
}
928+
}
929+
}
930+
Trunc(bcx, val.get(), Type::i1(bcx.ccx()))
852931
} else {
853932
val
854933
}
@@ -1709,8 +1788,7 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
17091788
disr: ty::Disr,
17101789
args: callee::CallArgs,
17111790
dest: expr::Dest,
1712-
debug_loc: DebugLoc)
1713-
-> Result<'blk, 'tcx> {
1791+
debug_loc: DebugLoc) -> Result<'blk, 'tcx> {
17141792

17151793
let ccx = bcx.fcx.ccx;
17161794

@@ -1753,6 +1831,12 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
17531831
}
17541832
}
17551833

1834+
let llretval = if type_is_immediate(bcx.ccx(), result_ty) {
1835+
load_ty(bcx, llresult, result_ty)
1836+
} else {
1837+
C_undef(type_of::type_of(bcx.ccx(), result_ty))
1838+
};
1839+
17561840
// If the caller doesn't care about the result
17571841
// drop the temporary we made
17581842
let bcx = match dest {
@@ -1766,7 +1850,8 @@ pub fn trans_named_tuple_constructor<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
17661850
}
17671851
};
17681852

1769-
Result::new(bcx, llresult)
1853+
1854+
Result::new(bcx, llretval)
17701855
}
17711856

17721857
pub fn trans_tuple_struct<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,

src/librustc_trans/trans/basic_block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use llvm::BasicBlockRef;
1313
use trans::value::{Users, Value};
1414
use std::iter::{Filter, Map};
1515

16-
#[derive(Copy, Clone)]
16+
#[derive(Copy, Clone, PartialEq)]
1717
pub struct BasicBlock(pub BasicBlockRef);
1818

1919
pub type Preds = Map<Filter<Users, fn(&Value) -> bool>, fn(Value) -> BasicBlock>;

src/librustc_trans/trans/builder.rs

+19-5
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use llvm::{Opcode, IntPredicate, RealPredicate, False};
1616
use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef};
1717
use trans::base;
1818
use trans::common::*;
19+
use trans::llrepr::LlvmRepr;
1920
use trans::machine::llalign_of_pref;
2021
use trans::type_::Type;
2122
use util::nodemap::FnvHashMap;
@@ -158,8 +159,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
158159
args: &[ValueRef],
159160
then: BasicBlockRef,
160161
catch: BasicBlockRef,
161-
attributes: Option<AttrBuilder>)
162-
-> ValueRef {
162+
attributes: Option<AttrBuilder>) -> ValueRef {
163163
self.count_insn("invoke");
164164

165165
debug!("Invoke {} with args ({})",
@@ -169,6 +169,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
169169
.collect::<Vec<String>>()
170170
.join(", "));
171171

172+
let llfnty = val_ty(llfn);
173+
174+
for (i, (pty, &a)) in llfnty.func_params().into_iter().zip(args.iter()).enumerate() {
175+
let aty = val_ty(a);
176+
assert!(pty == aty, "Type mismatch for arg {}. {} is not of type {}",
177+
i, self.ccx.tn().val_to_string(a), self.ccx.tn().type_to_string(pty));
178+
}
179+
172180
unsafe {
173181
let v = llvm::LLVMBuildInvoke(self.llbuilder,
174182
llfn,
@@ -499,9 +507,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
499507
}
500508

501509
pub fn volatile_store(&self, val: ValueRef, ptr: ValueRef) -> ValueRef {
502-
debug!("Store {} -> {}",
503-
self.ccx.tn().val_to_string(val),
504-
self.ccx.tn().val_to_string(ptr));
505510
assert!(!self.llbuilder.is_null());
506511
self.count_insn("store.volatile");
507512
unsafe {
@@ -524,6 +529,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
524529
}
525530

526531
pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef {
532+
debug!("GEP from {}, indices: {}",
533+
self.ccx.tn().val_to_string(ptr),
534+
indices.llrepr(self.ccx));
527535
self.count_insn("gep");
528536
unsafe {
529537
llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(),
@@ -535,6 +543,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
535543
// in C_i32()
536544
#[inline]
537545
pub fn gepi(&self, base: ValueRef, ixs: &[usize]) -> ValueRef {
546+
debug!("GEPi from {}, indices: {:?}",
547+
self.ccx.tn().val_to_string(base),
548+
ixs);
538549
// Small vector optimization. This should catch 100% of the cases that
539550
// we care about.
540551
if ixs.len() < 16 {
@@ -559,6 +570,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
559570
}
560571

561572
pub fn struct_gep(&self, ptr: ValueRef, idx: usize) -> ValueRef {
573+
debug!("Struct GEP from {}, index: {}",
574+
self.ccx.tn().val_to_string(ptr),
575+
idx);
562576
self.count_insn("structgep");
563577
unsafe {
564578
llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname())

0 commit comments

Comments
 (0)