InstEmitAluHelper.cs
1 using ARMeilleure.Decoders; 2 using ARMeilleure.IntermediateRepresentation; 3 using ARMeilleure.State; 4 using ARMeilleure.Translation; 5 using System; 6 using System.Diagnostics; 7 8 using static ARMeilleure.Instructions.InstEmitHelper; 9 using static ARMeilleure.IntermediateRepresentation.Operand.Factory; 10 11 namespace ARMeilleure.Instructions 12 { 13 static class InstEmitAluHelper 14 { 15 public static bool ShouldSetFlags(ArmEmitterContext context) 16 { 17 IOpCode32HasSetFlags op = (IOpCode32HasSetFlags)context.CurrOp; 18 19 if (op.SetFlags == null) 20 { 21 return !context.IsInIfThenBlock; 22 } 23 24 return op.SetFlags.Value; 25 } 26 27 public static void EmitNZFlagsCheck(ArmEmitterContext context, Operand d) 28 { 29 SetFlag(context, PState.NFlag, context.ICompareLess(d, Const(d.Type, 0))); 30 SetFlag(context, PState.ZFlag, context.ICompareEqual(d, Const(d.Type, 0))); 31 } 32 33 public static void EmitAdcsCCheck(ArmEmitterContext context, Operand n, Operand d) 34 { 35 // C = (Rd == Rn && CIn) || Rd < Rn 36 Operand cIn = GetFlag(PState.CFlag); 37 38 Operand cOut = context.BitwiseAnd(context.ICompareEqual(d, n), cIn); 39 40 cOut = context.BitwiseOr(cOut, context.ICompareLessUI(d, n)); 41 42 SetFlag(context, PState.CFlag, cOut); 43 } 44 45 public static void EmitAddsCCheck(ArmEmitterContext context, Operand n, Operand d) 46 { 47 // C = Rd < Rn 48 SetFlag(context, PState.CFlag, context.ICompareLessUI(d, n)); 49 } 50 51 public static void EmitAddsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d) 52 { 53 // V = (Rd ^ Rn) & ~(Rn ^ Rm) < 0 54 Operand vOut = context.BitwiseExclusiveOr(d, n); 55 56 vOut = context.BitwiseAnd(vOut, context.BitwiseNot(context.BitwiseExclusiveOr(n, m))); 57 58 vOut = context.ICompareLess(vOut, Const(vOut.Type, 0)); 59 60 SetFlag(context, PState.VFlag, vOut); 61 } 62 63 public static void EmitSbcsCCheck(ArmEmitterContext context, Operand n, Operand m) 64 { 65 // C = (Rn == Rm && CIn) || Rn > Rm 66 Operand cIn = GetFlag(PState.CFlag); 67 68 Operand cOut = context.BitwiseAnd(context.ICompareEqual(n, m), cIn); 69 70 cOut = context.BitwiseOr(cOut, context.ICompareGreaterUI(n, m)); 71 72 SetFlag(context, PState.CFlag, cOut); 73 } 74 75 public static void EmitSubsCCheck(ArmEmitterContext context, Operand n, Operand m) 76 { 77 // C = Rn >= Rm 78 SetFlag(context, PState.CFlag, context.ICompareGreaterOrEqualUI(n, m)); 79 } 80 81 public static void EmitSubsVCheck(ArmEmitterContext context, Operand n, Operand m, Operand d) 82 { 83 // V = (Rd ^ Rn) & (Rn ^ Rm) < 0 84 Operand vOut = context.BitwiseExclusiveOr(d, n); 85 86 vOut = context.BitwiseAnd(vOut, context.BitwiseExclusiveOr(n, m)); 87 88 vOut = context.ICompareLess(vOut, Const(vOut.Type, 0)); 89 90 SetFlag(context, PState.VFlag, vOut); 91 } 92 93 public static Operand EmitReverseBits32Op(ArmEmitterContext context, Operand op) 94 { 95 Debug.Assert(op.Type == OperandType.I32); 96 97 Operand val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xaaaaaaaau)), Const(1)), 98 context.ShiftLeft(context.BitwiseAnd(op, Const(0x55555555u)), Const(1))); 99 100 val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xccccccccu)), Const(2)), 101 context.ShiftLeft(context.BitwiseAnd(val, Const(0x33333333u)), Const(2))); 102 val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xf0f0f0f0u)), Const(4)), 103 context.ShiftLeft(context.BitwiseAnd(val, Const(0x0f0f0f0fu)), Const(4))); 104 val = context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(val, Const(0xff00ff00u)), Const(8)), 105 context.ShiftLeft(context.BitwiseAnd(val, Const(0x00ff00ffu)), Const(8))); 106 107 return context.BitwiseOr(context.ShiftRightUI(val, Const(16)), context.ShiftLeft(val, Const(16))); 108 } 109 110 public static Operand EmitReverseBytes16_64Op(ArmEmitterContext context, Operand op) 111 { 112 Debug.Assert(op.Type == OperandType.I64); 113 114 return context.BitwiseOr(context.ShiftRightUI(context.BitwiseAnd(op, Const(0xff00ff00ff00ff00ul)), Const(8)), 115 context.ShiftLeft(context.BitwiseAnd(op, Const(0x00ff00ff00ff00fful)), Const(8))); 116 } 117 118 public static Operand EmitReverseBytes16_32Op(ArmEmitterContext context, Operand op) 119 { 120 Debug.Assert(op.Type == OperandType.I32); 121 122 Operand val = EmitReverseBytes16_64Op(context, context.ZeroExtend32(OperandType.I64, op)); 123 124 return context.ConvertI64ToI32(val); 125 } 126 127 private static void EmitAluWritePc(ArmEmitterContext context, Operand value) 128 { 129 Debug.Assert(value.Type == OperandType.I32); 130 131 if (((OpCode32)context.CurrOp).IsThumb) 132 { 133 bool isReturn = IsA32Return(context); 134 if (!isReturn) 135 { 136 context.StoreToContext(); 137 } 138 139 InstEmitFlowHelper.EmitVirtualJump(context, value, isReturn); 140 } 141 else 142 { 143 EmitBxWritePc(context, value); 144 } 145 } 146 147 public static void EmitGenericAluStoreA32(ArmEmitterContext context, int rd, bool setFlags, Operand value) 148 { 149 Debug.Assert(value.Type == OperandType.I32); 150 151 if (rd == RegisterAlias.Aarch32Pc && setFlags) 152 { 153 if (setFlags) 154 { 155 // TODO: Load SPSR etc. 156 157 EmitBxWritePc(context, value); 158 } 159 else 160 { 161 EmitAluWritePc(context, value); 162 } 163 } 164 else 165 { 166 SetIntA32(context, rd, value); 167 } 168 } 169 170 public static Operand GetAluN(ArmEmitterContext context) 171 { 172 if (context.CurrOp is IOpCodeAlu op) 173 { 174 if (op.DataOp == DataOp.Logical || op is IOpCodeAluRs) 175 { 176 return GetIntOrZR(context, op.Rn); 177 } 178 else 179 { 180 return GetIntOrSP(context, op.Rn); 181 } 182 } 183 else if (context.CurrOp is IOpCode32Alu op32) 184 { 185 return GetIntA32(context, op32.Rn); 186 } 187 else 188 { 189 throw InvalidOpCodeType(context.CurrOp); 190 } 191 } 192 193 public static Operand GetAluM(ArmEmitterContext context, bool setCarry = true) 194 { 195 switch (context.CurrOp) 196 { 197 // ARM32. 198 case IOpCode32AluImm op: 199 { 200 if (ShouldSetFlags(context) && op.IsRotated && setCarry) 201 { 202 SetFlag(context, PState.CFlag, Const((uint)op.Immediate >> 31)); 203 } 204 205 return Const(op.Immediate); 206 } 207 208 case IOpCode32AluImm16 op: 209 return Const(op.Immediate); 210 211 case IOpCode32AluRsImm op: 212 return GetMShiftedByImmediate(context, op, setCarry); 213 case IOpCode32AluRsReg op: 214 return GetMShiftedByReg(context, op, setCarry); 215 216 case IOpCode32AluReg op: 217 return GetIntA32(context, op.Rm); 218 219 // ARM64. 220 case IOpCodeAluImm op: 221 { 222 if (op.GetOperandType() == OperandType.I32) 223 { 224 return Const((int)op.Immediate); 225 } 226 else 227 { 228 return Const(op.Immediate); 229 } 230 } 231 232 case IOpCodeAluRs op: 233 { 234 Operand value = GetIntOrZR(context, op.Rm); 235 236 switch (op.ShiftType) 237 { 238 case ShiftType.Lsl: 239 value = context.ShiftLeft(value, Const(op.Shift)); 240 break; 241 case ShiftType.Lsr: 242 value = context.ShiftRightUI(value, Const(op.Shift)); 243 break; 244 case ShiftType.Asr: 245 value = context.ShiftRightSI(value, Const(op.Shift)); 246 break; 247 case ShiftType.Ror: 248 value = context.RotateRight(value, Const(op.Shift)); 249 break; 250 } 251 252 return value; 253 } 254 255 case IOpCodeAluRx op: 256 { 257 Operand value = GetExtendedM(context, op.Rm, op.IntType); 258 259 value = context.ShiftLeft(value, Const(op.Shift)); 260 261 return value; 262 } 263 264 default: 265 throw InvalidOpCodeType(context.CurrOp); 266 } 267 } 268 269 private static Exception InvalidOpCodeType(OpCode opCode) 270 { 271 return new InvalidOperationException($"Invalid OpCode type \"{opCode?.GetType().Name ?? "null"}\"."); 272 } 273 274 // ARM32 helpers. 275 public static Operand GetMShiftedByImmediate(ArmEmitterContext context, IOpCode32AluRsImm op, bool setCarry) 276 { 277 Operand m = GetIntA32(context, op.Rm); 278 279 int shift = op.Immediate; 280 281 if (shift == 0) 282 { 283 switch (op.ShiftType) 284 { 285 case ShiftType.Lsr: 286 shift = 32; 287 break; 288 case ShiftType.Asr: 289 shift = 32; 290 break; 291 case ShiftType.Ror: 292 shift = 1; 293 break; 294 } 295 } 296 297 if (shift != 0) 298 { 299 setCarry &= ShouldSetFlags(context); 300 301 switch (op.ShiftType) 302 { 303 case ShiftType.Lsl: 304 m = GetLslC(context, m, setCarry, shift); 305 break; 306 case ShiftType.Lsr: 307 m = GetLsrC(context, m, setCarry, shift); 308 break; 309 case ShiftType.Asr: 310 m = GetAsrC(context, m, setCarry, shift); 311 break; 312 case ShiftType.Ror: 313 if (op.Immediate != 0) 314 { 315 m = GetRorC(context, m, setCarry, shift); 316 } 317 else 318 { 319 m = GetRrxC(context, m, setCarry); 320 } 321 break; 322 } 323 } 324 325 return m; 326 } 327 328 public static int DecodeImmShift(ShiftType shiftType, int shift) 329 { 330 if (shift == 0) 331 { 332 switch (shiftType) 333 { 334 case ShiftType.Lsr: 335 shift = 32; 336 break; 337 case ShiftType.Asr: 338 shift = 32; 339 break; 340 case ShiftType.Ror: 341 shift = 1; 342 break; 343 } 344 } 345 346 return shift; 347 } 348 349 public static Operand GetMShiftedByReg(ArmEmitterContext context, IOpCode32AluRsReg op, bool setCarry) 350 { 351 Operand m = GetIntA32(context, op.Rm); 352 Operand s = context.ZeroExtend8(OperandType.I32, GetIntA32(context, op.Rs)); 353 Operand shiftIsZero = context.ICompareEqual(s, Const(0)); 354 355 Operand zeroResult = m; 356 Operand shiftResult = m; 357 358 setCarry &= ShouldSetFlags(context); 359 360 switch (op.ShiftType) 361 { 362 case ShiftType.Lsl: 363 shiftResult = EmitLslC(context, m, setCarry, s, shiftIsZero); 364 break; 365 case ShiftType.Lsr: 366 shiftResult = EmitLsrC(context, m, setCarry, s, shiftIsZero); 367 break; 368 case ShiftType.Asr: 369 shiftResult = EmitAsrC(context, m, setCarry, s, shiftIsZero); 370 break; 371 case ShiftType.Ror: 372 shiftResult = EmitRorC(context, m, setCarry, s, shiftIsZero); 373 break; 374 } 375 376 return context.ConditionalSelect(shiftIsZero, zeroResult, shiftResult); 377 } 378 379 public static void EmitIfHelper(ArmEmitterContext context, Operand boolValue, Action action, bool expected = true) 380 { 381 Debug.Assert(boolValue.Type == OperandType.I32); 382 383 Operand endLabel = Label(); 384 385 if (expected) 386 { 387 context.BranchIfFalse(endLabel, boolValue); 388 } 389 else 390 { 391 context.BranchIfTrue(endLabel, boolValue); 392 } 393 394 action(); 395 396 context.MarkLabel(endLabel); 397 } 398 399 public static Operand EmitLslC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) 400 { 401 Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32); 402 403 Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); 404 Operand result = context.ShiftLeft(m, shift); 405 if (setCarry) 406 { 407 EmitIfHelper(context, shiftIsZero, () => 408 { 409 Operand cOut = context.ShiftRightUI(m, context.Subtract(Const(32), shift)); 410 411 cOut = context.BitwiseAnd(cOut, Const(1)); 412 cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); 413 414 SetFlag(context, PState.CFlag, cOut); 415 }, false); 416 } 417 418 return context.ConditionalSelect(shiftLarge, Const(0), result); 419 } 420 421 public static Operand GetLslC(ArmEmitterContext context, Operand m, bool setCarry, int shift) 422 { 423 Debug.Assert(m.Type == OperandType.I32); 424 425 if ((uint)shift > 32) 426 { 427 return GetShiftByMoreThan32(context, setCarry); 428 } 429 else if (shift == 32) 430 { 431 if (setCarry) 432 { 433 SetCarryMLsb(context, m); 434 } 435 436 return Const(0); 437 } 438 else 439 { 440 if (setCarry) 441 { 442 Operand cOut = context.ShiftRightUI(m, Const(32 - shift)); 443 444 cOut = context.BitwiseAnd(cOut, Const(1)); 445 446 SetFlag(context, PState.CFlag, cOut); 447 } 448 449 return context.ShiftLeft(m, Const(shift)); 450 } 451 } 452 453 public static Operand EmitLsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) 454 { 455 Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32); 456 457 Operand shiftLarge = context.ICompareGreaterOrEqual(shift, Const(32)); 458 Operand result = context.ShiftRightUI(m, shift); 459 if (setCarry) 460 { 461 EmitIfHelper(context, shiftIsZero, () => 462 { 463 Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); 464 465 cOut = context.BitwiseAnd(cOut, Const(1)); 466 cOut = context.ConditionalSelect(context.ICompareGreater(shift, Const(32)), Const(0), cOut); 467 468 SetFlag(context, PState.CFlag, cOut); 469 }, false); 470 } 471 472 return context.ConditionalSelect(shiftLarge, Const(0), result); 473 } 474 475 public static Operand GetLsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift) 476 { 477 Debug.Assert(m.Type == OperandType.I32); 478 479 if ((uint)shift > 32) 480 { 481 return GetShiftByMoreThan32(context, setCarry); 482 } 483 else if (shift == 32) 484 { 485 if (setCarry) 486 { 487 SetCarryMMsb(context, m); 488 } 489 490 return Const(0); 491 } 492 else 493 { 494 if (setCarry) 495 { 496 SetCarryMShrOut(context, m, shift); 497 } 498 499 return context.ShiftRightUI(m, Const(shift)); 500 } 501 } 502 503 private static Operand GetShiftByMoreThan32(ArmEmitterContext context, bool setCarry) 504 { 505 if (setCarry) 506 { 507 SetFlag(context, PState.CFlag, Const(0)); 508 } 509 510 return Const(0); 511 } 512 513 public static Operand EmitAsrC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) 514 { 515 Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32); 516 517 Operand l32Result; 518 Operand ge32Result; 519 520 Operand less32 = context.ICompareLess(shift, Const(32)); 521 522 ge32Result = context.ShiftRightSI(m, Const(31)); 523 524 if (setCarry) 525 { 526 EmitIfHelper(context, context.BitwiseOr(less32, shiftIsZero), () => 527 { 528 SetCarryMLsb(context, ge32Result); 529 }, false); 530 } 531 532 l32Result = context.ShiftRightSI(m, shift); 533 if (setCarry) 534 { 535 EmitIfHelper(context, context.BitwiseAnd(less32, context.BitwiseNot(shiftIsZero)), () => 536 { 537 Operand cOut = context.ShiftRightUI(m, context.Subtract(shift, Const(1))); 538 539 cOut = context.BitwiseAnd(cOut, Const(1)); 540 541 SetFlag(context, PState.CFlag, cOut); 542 }); 543 } 544 545 return context.ConditionalSelect(less32, l32Result, ge32Result); 546 } 547 548 public static Operand GetAsrC(ArmEmitterContext context, Operand m, bool setCarry, int shift) 549 { 550 Debug.Assert(m.Type == OperandType.I32); 551 552 if ((uint)shift >= 32) 553 { 554 m = context.ShiftRightSI(m, Const(31)); 555 556 if (setCarry) 557 { 558 SetCarryMLsb(context, m); 559 } 560 561 return m; 562 } 563 else 564 { 565 if (setCarry) 566 { 567 SetCarryMShrOut(context, m, shift); 568 } 569 570 return context.ShiftRightSI(m, Const(shift)); 571 } 572 } 573 574 public static Operand EmitRorC(ArmEmitterContext context, Operand m, bool setCarry, Operand shift, Operand shiftIsZero) 575 { 576 Debug.Assert(m.Type == OperandType.I32 && shift.Type == OperandType.I32 && shiftIsZero.Type == OperandType.I32); 577 578 shift = context.BitwiseAnd(shift, Const(0x1f)); 579 m = context.RotateRight(m, shift); 580 581 if (setCarry) 582 { 583 EmitIfHelper(context, shiftIsZero, () => 584 { 585 SetCarryMMsb(context, m); 586 }, false); 587 } 588 589 return m; 590 } 591 592 public static Operand GetRorC(ArmEmitterContext context, Operand m, bool setCarry, int shift) 593 { 594 Debug.Assert(m.Type == OperandType.I32); 595 596 shift &= 0x1f; 597 598 m = context.RotateRight(m, Const(shift)); 599 600 if (setCarry) 601 { 602 SetCarryMMsb(context, m); 603 } 604 605 return m; 606 } 607 608 public static Operand GetRrxC(ArmEmitterContext context, Operand m, bool setCarry) 609 { 610 Debug.Assert(m.Type == OperandType.I32); 611 612 // Rotate right by 1 with carry. 613 Operand cIn = context.Copy(GetFlag(PState.CFlag)); 614 615 if (setCarry) 616 { 617 SetCarryMLsb(context, m); 618 } 619 620 m = context.ShiftRightUI(m, Const(1)); 621 622 m = context.BitwiseOr(m, context.ShiftLeft(cIn, Const(31))); 623 624 return m; 625 } 626 627 private static void SetCarryMLsb(ArmEmitterContext context, Operand m) 628 { 629 Debug.Assert(m.Type == OperandType.I32); 630 631 SetFlag(context, PState.CFlag, context.BitwiseAnd(m, Const(1))); 632 } 633 634 private static void SetCarryMMsb(ArmEmitterContext context, Operand m) 635 { 636 Debug.Assert(m.Type == OperandType.I32); 637 638 SetFlag(context, PState.CFlag, context.ShiftRightUI(m, Const(31))); 639 } 640 641 private static void SetCarryMShrOut(ArmEmitterContext context, Operand m, int shift) 642 { 643 Debug.Assert(m.Type == OperandType.I32); 644 645 Operand cOut = context.ShiftRightUI(m, Const(shift - 1)); 646 647 cOut = context.BitwiseAnd(cOut, Const(1)); 648 649 SetFlag(context, PState.CFlag, cOut); 650 } 651 } 652 }