@@ -13,6 +13,7 @@ use std::{
1313 cell:: RefCell , collections:: { BTreeSet , HashMap , HashSet , VecDeque } , ffi:: { c_void, c_uint, c_int, CStr } , fmt:: Display , mem:: { align_of, size_of} , ptr, slice:: Iter
1414} ;
1515use crate :: hir_type:: { Type , types} ;
16+ use crate :: hir_effect:: { Effect , abstract_heaps, effects} ;
1617use crate :: bitset:: BitSet ;
1718use crate :: profile:: { TypeDistributionSummary , ProfiledType } ;
1819use crate :: stats:: Counter ;
@@ -1053,61 +1054,167 @@ impl Insn {
10531054 InsnPrinter { inner : self . clone ( ) , ptr_map, iseq }
10541055 }
10551056
1056- /// Return true if the instruction needs to be kept around. For example, if the instruction
1057- /// might have a side effect, or if the instruction may raise an exception.
1058- fn has_effects ( & self ) -> bool {
1059- match self {
1060- Insn :: Const { .. } => false ,
1061- Insn :: Param => false ,
1062- Insn :: StringCopy { .. } => false ,
1063- Insn :: NewArray { .. } => false ,
1064- // NewHash's operands may be hashed and compared for equality, which could have
1065- // side-effects.
1066- Insn :: NewHash { elements, .. } => !elements. is_empty ( ) ,
1067- Insn :: ArrayLength { .. } => false ,
1068- Insn :: ArrayDup { .. } => false ,
1069- Insn :: HashDup { .. } => false ,
1070- Insn :: Test { .. } => false ,
1071- Insn :: Snapshot { .. } => false ,
1072- Insn :: FixnumAdd { .. } => false ,
1073- Insn :: FixnumSub { .. } => false ,
1074- Insn :: FixnumMult { .. } => false ,
1075- // TODO(max): Consider adding a Guard that the rhs is non-zero before Div and Mod
1076- // Div *is* critical unless we can prove the right hand side != 0
1077- // Mod *is* critical unless we can prove the right hand side != 0
1078- Insn :: FixnumEq { .. } => false ,
1079- Insn :: FixnumNeq { .. } => false ,
1080- Insn :: FixnumLt { .. } => false ,
1081- Insn :: FixnumLe { .. } => false ,
1082- Insn :: FixnumGt { .. } => false ,
1083- Insn :: FixnumGe { .. } => false ,
1084- Insn :: FixnumAnd { .. } => false ,
1085- Insn :: FixnumOr { .. } => false ,
1086- Insn :: FixnumXor { .. } => false ,
1087- Insn :: FixnumLShift { .. } => false ,
1088- Insn :: FixnumRShift { .. } => false ,
1089- Insn :: FixnumAref { .. } => false ,
1090- Insn :: GetLocal { .. } => false ,
1091- Insn :: IsNil { .. } => false ,
1092- Insn :: LoadPC => false ,
1093- Insn :: LoadEC => false ,
1094- Insn :: LoadSelf => false ,
1095- Insn :: LoadField { .. } => false ,
1096- Insn :: CCall { elidable, .. } => !elidable,
1097- Insn :: CCallWithFrame { elidable, .. } => !elidable,
1098- Insn :: ObjectAllocClass { .. } => false ,
1099- // TODO: NewRange is effects free if we can prove the two ends to be Fixnum,
1100- // but we don't have type information here in `impl Insn`. See rb_range_new().
1101- Insn :: NewRange { .. } => true ,
1102- Insn :: NewRangeFixnum { .. } => false ,
1103- Insn :: StringGetbyte { .. } => false ,
1104- Insn :: IsBlockGiven => false ,
1105- Insn :: BoxFixnum { .. } => false ,
1106- Insn :: BoxBool { .. } => false ,
1107- Insn :: IsBitEqual { .. } => false ,
1108- Insn :: IsA { .. } => false ,
1109- _ => true ,
1110- }
1057+ // TODO(Jacob): Model SP. ie, all allocations modify stack size but using the effect for stack modification feels excessive
1058+ // TODO(Jacob): Add sideeffect failure bit
1059+ fn effects_of ( & self ) -> Effect {
1060+ const allocates: Effect = Effect :: read_write ( abstract_heaps:: PC . union ( abstract_heaps:: Allocator ) , abstract_heaps:: Allocator ) ;
1061+ match & self {
1062+ Insn :: Const { .. } => effects:: Empty ,
1063+ Insn :: Param { .. } => effects:: Empty ,
1064+ Insn :: StringCopy { .. } => allocates,
1065+ Insn :: StringIntern { .. } => effects:: Any ,
1066+ Insn :: StringConcat { .. } => effects:: Any ,
1067+ Insn :: StringGetbyte { .. } => Effect :: read_write ( abstract_heaps:: Other , abstract_heaps:: Empty ) ,
1068+ Insn :: StringSetbyteFixnum { .. } => effects:: Any ,
1069+ Insn :: StringAppend { .. } => effects:: Any ,
1070+ Insn :: StringAppendCodepoint { .. } => effects:: Any ,
1071+ Insn :: ToRegexp { .. } => effects:: Any ,
1072+ Insn :: PutSpecialObject { .. } => effects:: Any ,
1073+ Insn :: ToArray { .. } => effects:: Any ,
1074+ Insn :: ToNewArray { .. } => effects:: Any ,
1075+ Insn :: NewArray { .. } => allocates,
1076+ Insn :: NewHash { elements, .. } => {
1077+ // NewHash's operands may be hashed and compared for equality, which could have
1078+ // side-effects. Empty hashes are definitely elidable.
1079+ if elements. is_empty ( ) {
1080+ Effect :: write ( abstract_heaps:: Allocator )
1081+ }
1082+ else {
1083+ effects:: Any
1084+ }
1085+ } ,
1086+ Insn :: NewRange { .. } => effects:: Any ,
1087+ Insn :: NewRangeFixnum { .. } => allocates,
1088+ Insn :: ArrayDup { .. } => allocates,
1089+ Insn :: ArrayHash { .. } => effects:: Any ,
1090+ Insn :: ArrayMax { .. } => effects:: Any ,
1091+ Insn :: ArrayInclude { .. } => effects:: Any ,
1092+ Insn :: ArrayPackBuffer { .. } => effects:: Any ,
1093+ Insn :: DupArrayInclude { .. } => effects:: Any ,
1094+ Insn :: ArrayExtend { .. } => effects:: Any ,
1095+ Insn :: ArrayPush { .. } => effects:: Any ,
1096+ Insn :: ArrayAref { .. } => effects:: Any ,
1097+ Insn :: ArrayAset { .. } => effects:: Any ,
1098+ Insn :: ArrayPop { .. } => effects:: Any ,
1099+ Insn :: ArrayLength { .. } => Effect :: write ( abstract_heaps:: Empty ) ,
1100+ Insn :: HashAref { .. } => effects:: Any ,
1101+ Insn :: HashAset { .. } => effects:: Any ,
1102+ Insn :: HashDup { .. } => allocates,
1103+ Insn :: ObjectAlloc { .. } => effects:: Any ,
1104+ Insn :: ObjectAllocClass { .. } => allocates,
1105+ Insn :: Test { .. } => effects:: Empty ,
1106+ Insn :: IsNil { .. } => effects:: Empty ,
1107+ Insn :: IsMethodCfunc { .. } => effects:: Any ,
1108+ Insn :: IsBitEqual { .. } => effects:: Empty ,
1109+ Insn :: IsBitNotEqual { .. } => effects:: Any ,
1110+ Insn :: BoxBool { .. } => effects:: Empty ,
1111+ Insn :: BoxFixnum { .. } => effects:: Empty ,
1112+ Insn :: UnboxFixnum { .. } => effects:: Any ,
1113+ Insn :: FixnumAref { .. } => effects:: Empty ,
1114+ Insn :: Defined { .. } => effects:: Any ,
1115+ Insn :: GetConstantPath { .. } => effects:: Any ,
1116+ Insn :: IsBlockGiven { .. } => Effect :: read_write ( abstract_heaps:: Other , abstract_heaps:: Empty ) ,
1117+ Insn :: FixnumBitCheck { .. } => effects:: Any ,
1118+ Insn :: IsA { .. } => effects:: Empty ,
1119+ Insn :: GetGlobal { .. } => effects:: Any ,
1120+ Insn :: SetGlobal { .. } => effects:: Any ,
1121+ Insn :: GetIvar { .. } => effects:: Any ,
1122+ Insn :: SetIvar { .. } => effects:: Any ,
1123+ Insn :: DefinedIvar { .. } => effects:: Any ,
1124+ Insn :: LoadPC { .. } => Effect :: read_write ( abstract_heaps:: PC , abstract_heaps:: Empty ) ,
1125+ Insn :: LoadEC { .. } => effects:: Empty ,
1126+ Insn :: LoadSelf { .. } => Effect :: read_write ( abstract_heaps:: Frame , abstract_heaps:: Empty ) ,
1127+ Insn :: LoadField { .. } => Effect :: read_write ( abstract_heaps:: Other , abstract_heaps:: Empty ) ,
1128+ Insn :: StoreField { .. } => effects:: Any ,
1129+ Insn :: WriteBarrier { .. } => effects:: Any ,
1130+ Insn :: GetLocal { .. } => Effect :: read_write ( abstract_heaps:: Locals , abstract_heaps:: Empty ) ,
1131+ Insn :: SetLocal { .. } => effects:: Any ,
1132+ Insn :: GetSpecialSymbol { .. } => effects:: Any ,
1133+ Insn :: GetSpecialNumber { .. } => effects:: Any ,
1134+ Insn :: GetClassVar { .. } => effects:: Any ,
1135+ Insn :: SetClassVar { .. } => effects:: Any ,
1136+ Insn :: Snapshot { .. } => effects:: Empty ,
1137+ Insn :: Jump ( _) => effects:: Any ,
1138+ Insn :: IfTrue { .. } => effects:: Any ,
1139+ Insn :: IfFalse { .. } => effects:: Any ,
1140+ Insn :: CCall { elidable, .. } => {
1141+ if * elidable {
1142+ Effect :: write ( abstract_heaps:: Allocator )
1143+ }
1144+ else {
1145+ effects:: Any
1146+ }
1147+ } ,
1148+ Insn :: CCallWithFrame { elidable, .. } => {
1149+ if * elidable {
1150+ Effect :: write ( abstract_heaps:: Allocator )
1151+ }
1152+ else {
1153+ effects:: Any
1154+ }
1155+ } ,
1156+ Insn :: CCallVariadic { .. } => effects:: Any ,
1157+ Insn :: SendWithoutBlock { .. } => effects:: Any ,
1158+ Insn :: Send { .. } => effects:: Any ,
1159+ Insn :: SendForward { .. } => effects:: Any ,
1160+ Insn :: InvokeSuper { .. } => effects:: Any ,
1161+ Insn :: InvokeBlock { .. } => effects:: Any ,
1162+ Insn :: SendWithoutBlockDirect { .. } => effects:: Any ,
1163+ Insn :: InvokeBuiltin { .. } => effects:: Any ,
1164+ Insn :: EntryPoint { .. } => effects:: Any ,
1165+ Insn :: Return { .. } => effects:: Any ,
1166+ Insn :: Throw { .. } => effects:: Any ,
1167+ Insn :: FixnumAdd { .. } => effects:: Empty ,
1168+ Insn :: FixnumSub { .. } => effects:: Empty ,
1169+ Insn :: FixnumMult { .. } => effects:: Empty ,
1170+ Insn :: FixnumDiv { .. } => effects:: Any ,
1171+ Insn :: FixnumMod { .. } => effects:: Any ,
1172+ Insn :: FixnumEq { .. } => effects:: Empty ,
1173+ Insn :: FixnumNeq { .. } => effects:: Empty ,
1174+ Insn :: FixnumLt { .. } => effects:: Empty ,
1175+ Insn :: FixnumLe { .. } => effects:: Empty ,
1176+ Insn :: FixnumGt { .. } => effects:: Empty ,
1177+ Insn :: FixnumGe { .. } => effects:: Empty ,
1178+ Insn :: FixnumAnd { .. } => effects:: Empty ,
1179+ Insn :: FixnumOr { .. } => effects:: Empty ,
1180+ Insn :: FixnumXor { .. } => effects:: Empty ,
1181+ Insn :: FixnumLShift { .. } => effects:: Empty ,
1182+ Insn :: FixnumRShift { .. } => effects:: Empty ,
1183+ Insn :: ObjToString { .. } => effects:: Any ,
1184+ Insn :: AnyToString { .. } => effects:: Any ,
1185+ Insn :: GuardType { .. } => effects:: Any ,
1186+ Insn :: GuardTypeNot { .. } => effects:: Any ,
1187+ Insn :: GuardBitEquals { .. } => effects:: Any ,
1188+ Insn :: GuardShape { .. } => effects:: Any ,
1189+ Insn :: GuardBlockParamProxy { .. } => effects:: Any ,
1190+ Insn :: GuardNotFrozen { .. } => effects:: Any ,
1191+ Insn :: GuardNotShared { .. } => effects:: Any ,
1192+ Insn :: GuardGreaterEq { .. } => effects:: Any ,
1193+ Insn :: GuardSuperMethodEntry { .. } => effects:: Any ,
1194+ Insn :: GetBlockHandler { .. } => effects:: Any ,
1195+ Insn :: GuardLess { .. } => effects:: Any ,
1196+ Insn :: PatchPoint { .. } => effects:: Any ,
1197+ Insn :: SideExit { .. } => effects:: Any ,
1198+ Insn :: IncrCounter ( _) => effects:: Any ,
1199+ Insn :: IncrCounterPtr { .. } => effects:: Any ,
1200+ Insn :: CheckInterrupts { .. } => effects:: Any ,
1201+ }
1202+ }
1203+
1204+ /// Return true if we can safely omit the instruction. This occurs when one of the following
1205+ /// conditions are met.
1206+ /// 1. The instruction does not write anything.
1207+ /// 2. The instruction only allocates and writes nothing else.
1208+ /// Calling the effects of our instruction `insn_effects`, we need:
1209+ /// `effects::Empty` to include `insn_effects.write` or `effects::Allocator` to include
1210+ /// `insn_effects.write`.
1211+ /// We can simplify this to `effects::Empty.union(effects::Allocator).includes(insn_effects.write)`.
1212+ /// But the union of `Allocator` and `Empty` is simply `Allocator`, so our entire function
1213+ /// collapses to `effects::Allocator.includes(insn_effects.write)`.
1214+ /// Note: These are restrictions on the `write` `EffectSet` only. Even instructions with
1215+ /// `read: effects::Any` could potentially be omitted.
1216+ fn is_elidable ( & self ) -> bool {
1217+ abstract_heaps:: Allocator . includes ( self . effects_of ( ) . write_bits ( ) )
11111218 }
11121219}
11131220
@@ -4388,8 +4495,7 @@ impl Function {
43884495 // otherwise necessary to keep around
43894496 for block_id in & rpo {
43904497 for insn_id in & self . blocks [ block_id. 0 ] . insns {
4391- let insn = & self . insns [ insn_id. 0 ] ;
4392- if insn. has_effects ( ) {
4498+ if !& self . insns [ insn_id. 0 ] . is_elidable ( ) {
43934499 worklist. push_back ( * insn_id) ;
43944500 }
43954501 }
0 commit comments