Add with carry
(&mut self, left: JitTemp, right: JitTemp, carry: JitTemp)
| 121 | |
| 122 | /// Add with carry |
| 123 | pub fn adc<const FLAG:bool>(&mut self, left: JitTemp, right: JitTemp, carry: JitTemp) -> JitTemp { |
| 124 | use JitTemp::*; |
| 125 | use Flags::*; |
| 126 | let flags = vec![OF, SF, ZF, AF, PF, CF]; |
| 127 | match (left.clone(), right.clone(), carry.clone()) { |
| 128 | (Value(val_left, typ_left), Value(val_right, _), Flag(val_carry)) => { |
| 129 | let (res, flag) = self.builder.ins().iadd_carry(val_left, val_right, val_carry); |
| 130 | self.context.set_flags(flags, (val_left, val_right, flag)); |
| 131 | Value(res, typ_left) |
| 132 | }, |
| 133 | (_, _, carry) if carry.is_const() => { |
| 134 | // basically, this sucks a lot: |
| 135 | // iadd_carry is broken on cranelift (#2860), so we work around |
| 136 | // it by lowering to a normal iadd_cout in the CF=false case, |
| 137 | // and to two iadd_cout's in the CF=true case (and then have to |
| 138 | // fixup our carry flag so the spliced +1 doesn't break). |
| 139 | let carry = carry.into_usize(self.builder).unwrap(); |
| 140 | if carry == 0 { |
| 141 | return self.add::<FLAG>(left, right); |
| 142 | } else { |
| 143 | let left_plus_one = self.add::<FLAG>(left, Const8(1)); |
| 144 | // track if the +1 overflowed |
| 145 | let carried = self.context.check_flag(Flags::CF, true, self.builder); |
| 146 | |
| 147 | let res = self.add::<FLAG>(left_plus_one, right); |
| 148 | if(FLAG) { |
| 149 | // if the +1 definitely carried, we know that carry has to |
| 150 | // be set. |
| 151 | println!("carried = {:?}", carried); |
| 152 | if let JitTemp::Const64(1) = carried { |
| 153 | self.context.flags[Flags::CF as usize] = JitFlag::Known(1 << Flags::CF as usize); |
| 154 | } else if let JitTemp::Const64(0) = carried { |
| 155 | (); |
| 156 | } else if let JitTemp::Value(val, typ) = carried { |
| 157 | unimplemented!("double check this"); |
| 158 | // but if we don't know, then we have to OR it with |
| 159 | // the other carry flag (which sucks!) |
| 160 | let other_carried = self.context.check_flag(Flags::CF, true, self.builder); |
| 161 | let maybe_carried = self.bor(other_carried, carried); |
| 162 | self.context.set_flags(vec![Flags::CF], |
| 163 | (left_plus_one.into_ssa(typ, self.builder), |
| 164 | right.into_ssa(typ, self.builder), |
| 165 | maybe_carried.into_ssa(typ, self.builder))); |
| 166 | } else { |
| 167 | unimplemented!(); |
| 168 | } |
| 169 | } |
| 170 | return res; |
| 171 | } |
| 172 | }, |
| 173 | (left @ Value(_, _), right @ _, carry @ _) | |
| 174 | (left @ _, right @ Value(_, _), carry @ _) | |
| 175 | (left @ _, right @ _, carry @ Flag(_)) => { |
| 176 | let typ = Type::int((left.width() * 8) as u16).unwrap(); |
| 177 | let _left = left.into_ssa(typ, self.builder); |
| 178 | let _right = right.zero_extend(self.builder, left.width()).into_ssa(typ, self.builder); |
| 179 | let mut _carry = carry.into_ssa(types::IFLAGS, self.builder); |
| 180 | let (res, flag) = self.builder.ins().iadd_ifcarry(_left, _right, _carry); |