[vm/arm/arm64] Remove unpredictable (and sometimes illegal) instructions on ARM[64].
Also strengthen asserts to avoid regression.
Change-Id: I93ed9dac5cda9a01eba6ecdd19f78b11ed584114
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106904
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Samir Jindel <sjindel@google.com>
diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc
index 1c06796..0898d42 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.cc
+++ b/runtime/vm/compiler/assembler/assembler_arm.cc
@@ -131,7 +131,8 @@
Address ad) {
ASSERT(rd != kNoRegister);
ASSERT(cond != kNoCondition);
- ASSERT(!ad.has_writeback() || (ad.rn() != rd)); // Unpredictable.
+ // Unpredictable, illegal on some microarchitectures.
+ ASSERT(!ad.has_writeback() || (ad.rn() != rd));
int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | B26 |
(ad.kind() == Address::Immediate ? 0 : B25) |
@@ -146,6 +147,9 @@
Address ad) {
ASSERT(rd != kNoRegister);
ASSERT(cond != kNoCondition);
+ // Unpredictable, illegal on some microarchitectures.
+ ASSERT(!ad.has_writeback() || (ad.rn() != rd));
+
int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | mode |
ArmEncode::Rd(rd) | ad.encoding3();
Emit(encoding);
@@ -158,6 +162,8 @@
RegList regs) {
ASSERT(base != kNoRegister);
ASSERT(cond != kNoCondition);
+ // Unpredictable, illegal on some microarchitectures.
+ ASSERT(!Address::has_writeback(am) || !(regs & (1 << base)));
int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | B27 |
am | (load ? L : 0) | ArmEncode::Rn(base) | regs;
Emit(encoding);
diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h
index 19ba44f..13ea001 100644
--- a/runtime/vm/compiler/assembler/assembler_arm.h
+++ b/runtime/vm/compiler/assembler/assembler_arm.h
@@ -310,6 +310,21 @@
(mode() == NegPreIndex) || (mode() == NegPostIndex);
}
+ static bool has_writeback(BlockAddressMode am) {
+ switch (am) {
+ case DA:
+ case IA:
+ case DB:
+ case IB:
+ return false;
+ case DA_W:
+ case IA_W:
+ case DB_W:
+ case IB_W:
+ return true;
+ }
+ }
+
uint32_t encoding() const { return encoding_; }
// Encoding for addressing mode 3.
diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h
index b8808f5..26f8cb9 100644
--- a/runtime/vm/compiler/assembler/assembler_arm64.h
+++ b/runtime/vm/compiler/assembler/assembler_arm64.h
@@ -110,6 +110,17 @@
Unknown,
};
+ // If we are doing pre-/post-indexing, and the base and result registers are
+ // the same, then the result is unpredictable. This kind of instruction is
+ // actually illegal on some microarchitectures.
+ bool can_writeback_to(Register r) const {
+ if (type() == PreIndex || type() == PostIndex || type() == PairPreIndex ||
+ type() == PairPostIndex) {
+ return base() != r;
+ }
+ return true;
+ }
+
// Offset is in bytes. For the unsigned imm12 case, we unscale based on the
// operand size, and assert that offset is aligned accordingly.
// For the smaller signed imm9 case, the offset is the number of bytes, but
@@ -869,12 +880,6 @@
ASSERT(sz == kDoubleWord);
EmitLoadRegLiteral(LDRpc, rt, a, sz);
} else {
- // If we are doing pre-/post-indexing, and the base and result registers
- // are the same, then the result of the load will be clobbered by the
- // writeback, which is unlikely to be useful.
- ASSERT(((a.type() != Address::PreIndex) &&
- (a.type() != Address::PostIndex)) ||
- (rt != a.base()));
if (IsSignedOperand(sz)) {
EmitLoadStoreReg(LDRS, rt, a, sz);
} else {
@@ -2067,6 +2072,9 @@
Register rt,
Address a,
OperandSize sz) {
+ // Unpredictable, illegal on some microarchitectures.
+ ASSERT((op != LDR && op != STR && op != LDRS) || a.can_writeback_to(rt));
+
const int32_t size = Log2OperandSizeBytes(sz);
const int32_t encoding =
op | ((size & 0x3) << kSzShift) | Arm64Encode::Rt(rt) | a.encoding();
@@ -2089,6 +2097,10 @@
Register rt2,
Address a,
OperandSize sz) {
+ // Unpredictable, illegal on some microarchitectures.
+ ASSERT(a.can_writeback_to(rt) && a.can_writeback_to(rt2));
+ ASSERT(op != LDP || rt != rt2);
+
ASSERT((sz == kDoubleWord) || (sz == kWord) || (sz == kUnsignedWord));
ASSERT((rt != CSP) && (rt != R31));
ASSERT((rt2 != CSP) && (rt2 != R31));
diff --git a/runtime/vm/compiler/stub_code_compiler_arm64.cc b/runtime/vm/compiler/stub_code_compiler_arm64.cc
index 2f3b1b0..26d8c00 100644
--- a/runtime/vm/compiler/stub_code_compiler_arm64.cc
+++ b/runtime/vm/compiler/stub_code_compiler_arm64.cc
@@ -782,6 +782,12 @@
COMPILE_ASSERT(R25 > CODE_REG);
__ ldr(R25, Address(FP, 2 * target::kWordSize));
__ str(R25, Address(SP, -1 * target::kWordSize, Address::PreIndex));
+ } else if (r == R15) {
+ // Because we save registers in decreasing order, IP0 will already be
+ // saved.
+ COMPILE_ASSERT(IP0 == R16);
+ __ mov(IP0, R15);
+ __ str(IP0, Address(SP, -1 * target::kWordSize, Address::PreIndex));
} else {
__ str(r, Address(SP, -1 * target::kWordSize, Address::PreIndex));
}