MIPS[64]: Port '[turbofan] Use zr to store immediate zero'

Port 0322c20d17

Original commit message:
When storing an immediate integer or floating point zero, use the zero register
as the source value. This avoids the need to sometimes allocate a new register.

BUG=

Review-Url: https://codereview.chromium.org/2470133005
Cr-Commit-Position: refs/heads/master@{#40987}
This commit is contained in:
dusan.simicic 2016-11-15 03:57:13 -08:00 committed by Commit bot
parent 815eca573a
commit b725d5a797
7 changed files with 384 additions and 60 deletions

View File

@ -54,6 +54,14 @@ class MipsOperandConverter final : public InstructionOperandConverter {
return ToDoubleRegister(op); return ToDoubleRegister(op);
} }
Register InputOrZeroRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) {
DCHECK((InputInt32(index) == 0));
return zero_reg;
}
return InputRegister(index);
}
DoubleRegister InputOrZeroDoubleRegister(size_t index) { DoubleRegister InputOrZeroDoubleRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
@ -381,45 +389,48 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
__ bind(ool->exit()); \ __ bind(ool->exit()); \
} while (0) } while (0)
#define ASSEMBLE_CHECKED_STORE_FLOAT(width, asm_instr) \ #define ASSEMBLE_CHECKED_STORE_FLOAT(width, asm_instr) \
do { \ do { \
Label done; \ Label done; \
if (instr->InputAt(0)->IsRegister()) { \ if (instr->InputAt(0)->IsRegister()) { \
auto offset = i.InputRegister(0); \ auto offset = i.InputRegister(0); \
auto value = i.Input##width##Register(2); \ auto value = i.InputOrZero##width##Register(2); \
if (value.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) { \
__ Move(kDoubleRegZero, 0.0); \
} \
__ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \ __ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \
__ addu(kScratchReg, i.InputRegister(3), offset); \ __ addu(kScratchReg, i.InputRegister(3), offset); \
__ asm_instr(value, MemOperand(kScratchReg, 0)); \ __ asm_instr(value, MemOperand(kScratchReg, 0)); \
} else { \ } else { \
auto offset = i.InputOperand(0).immediate(); \ auto offset = i.InputOperand(0).immediate(); \
auto value = i.Input##width##Register(2); \ auto value = i.InputOrZero##width##Register(2); \
if (value.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) { \
__ Move(kDoubleRegZero, 0.0); \
} \
__ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \ __ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \
__ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \ __ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \
} \ } \
__ bind(&done); \ __ bind(&done); \
} while (0) } while (0)
#define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \ #define ASSEMBLE_CHECKED_STORE_INTEGER(asm_instr) \
do { \ do { \
Label done; \ Label done; \
if (instr->InputAt(0)->IsRegister()) { \ if (instr->InputAt(0)->IsRegister()) { \
auto offset = i.InputRegister(0); \ auto offset = i.InputRegister(0); \
auto value = i.InputRegister(2); \ auto value = i.InputOrZeroRegister(2); \
__ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \ __ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \
__ addu(kScratchReg, i.InputRegister(3), offset); \ __ addu(kScratchReg, i.InputRegister(3), offset); \
__ asm_instr(value, MemOperand(kScratchReg, 0)); \ __ asm_instr(value, MemOperand(kScratchReg, 0)); \
} else { \ } else { \
auto offset = i.InputOperand(0).immediate(); \ auto offset = i.InputOperand(0).immediate(); \
auto value = i.InputRegister(2); \ auto value = i.InputOrZeroRegister(2); \
__ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \ __ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \
__ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \ __ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \
} \ } \
__ bind(&done); \ __ bind(&done); \
} while (0) } while (0)
#define ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(mode) \ #define ASSEMBLE_ROUND_DOUBLE_TO_DOUBLE(mode) \
if (IsMipsArchVariant(kMips32r6)) { \ if (IsMipsArchVariant(kMips32r6)) { \
__ cfc1(kScratchReg, FCSR); \ __ cfc1(kScratchReg, FCSR); \
@ -478,11 +489,11 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
__ sync(); \ __ sync(); \
} while (0) } while (0)
#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \ #define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
do { \ do { \
__ sync(); \ __ sync(); \
__ asm_instr(i.InputRegister(2), i.MemoryOperand()); \ __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
__ sync(); \ __ sync(); \
} while (0) } while (0)
#define ASSEMBLE_IEEE754_BINOP(name) \ #define ASSEMBLE_IEEE754_BINOP(name) \
@ -1402,7 +1413,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ lb(i.OutputRegister(), i.MemoryOperand()); __ lb(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMipsSb: case kMipsSb:
__ sb(i.InputRegister(2), i.MemoryOperand()); __ sb(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMipsLhu: case kMipsLhu:
__ lhu(i.OutputRegister(), i.MemoryOperand()); __ lhu(i.OutputRegister(), i.MemoryOperand());
@ -1417,10 +1428,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Ulh(i.OutputRegister(), i.MemoryOperand()); __ Ulh(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMipsSh: case kMipsSh:
__ sh(i.InputRegister(2), i.MemoryOperand()); __ sh(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMipsUsh: case kMipsUsh:
__ Ush(i.InputRegister(2), i.MemoryOperand(), kScratchReg); __ Ush(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg);
break; break;
case kMipsLw: case kMipsLw:
__ lw(i.OutputRegister(), i.MemoryOperand()); __ lw(i.OutputRegister(), i.MemoryOperand());
@ -1429,10 +1440,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Ulw(i.OutputRegister(), i.MemoryOperand()); __ Ulw(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMipsSw: case kMipsSw:
__ sw(i.InputRegister(2), i.MemoryOperand()); __ sw(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMipsUsw: case kMipsUsw:
__ Usw(i.InputRegister(2), i.MemoryOperand()); __ Usw(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMipsLwc1: { case kMipsLwc1: {
__ lwc1(i.OutputSingleRegister(), i.MemoryOperand()); __ lwc1(i.OutputSingleRegister(), i.MemoryOperand());
@ -1445,13 +1456,21 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kMipsSwc1: { case kMipsSwc1: {
size_t index = 0; size_t index = 0;
MemOperand operand = i.MemoryOperand(&index); MemOperand operand = i.MemoryOperand(&index);
__ swc1(i.InputSingleRegister(index), operand); FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ swc1(ft, operand);
break; break;
} }
case kMipsUswc1: { case kMipsUswc1: {
size_t index = 0; size_t index = 0;
MemOperand operand = i.MemoryOperand(&index); MemOperand operand = i.MemoryOperand(&index);
__ Uswc1(i.InputSingleRegister(index), operand, kScratchReg); FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Uswc1(ft, operand, kScratchReg);
break; break;
} }
case kMipsLdc1: case kMipsLdc1:
@ -1460,12 +1479,22 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kMipsUldc1: case kMipsUldc1:
__ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg); __ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg);
break; break;
case kMipsSdc1: case kMipsSdc1: {
__ sdc1(i.InputDoubleRegister(2), i.MemoryOperand()); FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ sdc1(ft, i.MemoryOperand());
break; break;
case kMipsUsdc1: }
__ Usdc1(i.InputDoubleRegister(2), i.MemoryOperand(), kScratchReg); case kMipsUsdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Usdc1(ft, i.MemoryOperand(), kScratchReg);
break; break;
}
case kMipsPush: case kMipsPush:
if (instr->InputAt(0)->IsFPRegister()) { if (instr->InputAt(0)->IsFPRegister()) {
__ sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize)); __ sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));

View File

@ -31,6 +31,39 @@ class MipsOperandGenerator final : public OperandGenerator {
return UseRegister(node); return UseRegister(node);
} }
// Use the zero register if the node has the immediate value zero, otherwise
// assign a register.
InstructionOperand UseRegisterOrImmediateZero(Node* node) {
if ((IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) ||
(IsFloatConstant(node) &&
(bit_cast<int64_t>(GetFloatConstantValue(node)) == V8_INT64_C(0)))) {
return UseImmediate(node);
}
return UseRegister(node);
}
bool IsIntegerConstant(Node* node) {
return (node->opcode() == IrOpcode::kInt32Constant);
}
int64_t GetIntegerConstantValue(Node* node) {
DCHECK(node->opcode() == IrOpcode::kInt32Constant);
return OpParameter<int32_t>(node);
}
bool IsFloatConstant(Node* node) {
return (node->opcode() == IrOpcode::kFloat32Constant) ||
(node->opcode() == IrOpcode::kFloat64Constant);
}
double GetFloatConstantValue(Node* node) {
if (node->opcode() == IrOpcode::kFloat32Constant) {
return OpParameter<float>(node);
}
DCHECK_EQ(IrOpcode::kFloat64Constant, node->opcode());
return OpParameter<double>(node);
}
bool CanBeImmediate(Node* node, InstructionCode opcode) { bool CanBeImmediate(Node* node, InstructionCode opcode) {
Int32Matcher m(node); Int32Matcher m(node);
if (!m.HasValue()) return false; if (!m.HasValue()) return false;
@ -301,14 +334,15 @@ void InstructionSelector::VisitStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }
} }
@ -1233,14 +1267,15 @@ void InstructionSelector::VisitUnalignedStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }
@ -1332,7 +1367,7 @@ void InstructionSelector::VisitCheckedStore(Node* node) {
: g.UseRegister(length); : g.UseRegister(length);
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
offset_operand, length_operand, g.UseRegister(value), offset_operand, length_operand, g.UseRegisterOrImmediateZero(value),
g.UseRegister(buffer)); g.UseRegister(buffer));
} }
@ -1803,14 +1838,15 @@ void InstructionSelector::VisitAtomicStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMipsAdd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }

View File

@ -53,6 +53,14 @@ class MipsOperandConverter final : public InstructionOperandConverter {
return ToDoubleRegister(op); return ToDoubleRegister(op);
} }
Register InputOrZeroRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) {
DCHECK((InputInt32(index) == 0));
return zero_reg;
}
return InputRegister(index);
}
DoubleRegister InputOrZeroDoubleRegister(size_t index) { DoubleRegister InputOrZeroDoubleRegister(size_t index) {
if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero; if (instr_->InputAt(index)->IsImmediate()) return kDoubleRegZero;
@ -400,14 +408,20 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
Label done; \ Label done; \
if (instr->InputAt(0)->IsRegister()) { \ if (instr->InputAt(0)->IsRegister()) { \
auto offset = i.InputRegister(0); \ auto offset = i.InputRegister(0); \
auto value = i.Input##width##Register(2); \ auto value = i.InputOrZero##width##Register(2); \
if (value.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) { \
__ Move(kDoubleRegZero, 0.0); \
} \
__ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \ __ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \
__ And(kScratchReg, offset, Operand(0xffffffff)); \ __ And(kScratchReg, offset, Operand(0xffffffff)); \
__ Daddu(kScratchReg, i.InputRegister(3), kScratchReg); \ __ Daddu(kScratchReg, i.InputRegister(3), kScratchReg); \
__ asm_instr(value, MemOperand(kScratchReg, 0)); \ __ asm_instr(value, MemOperand(kScratchReg, 0)); \
} else { \ } else { \
int offset = static_cast<int>(i.InputOperand(0).immediate()); \ int offset = static_cast<int>(i.InputOperand(0).immediate()); \
auto value = i.Input##width##Register(2); \ auto value = i.InputOrZero##width##Register(2); \
if (value.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) { \
__ Move(kDoubleRegZero, 0.0); \
} \
__ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \ __ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \
__ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \ __ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \
} \ } \
@ -419,14 +433,14 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
Label done; \ Label done; \
if (instr->InputAt(0)->IsRegister()) { \ if (instr->InputAt(0)->IsRegister()) { \
auto offset = i.InputRegister(0); \ auto offset = i.InputRegister(0); \
auto value = i.InputRegister(2); \ auto value = i.InputOrZeroRegister(2); \
__ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \ __ Branch(USE_DELAY_SLOT, &done, hs, offset, i.InputOperand(1)); \
__ And(kScratchReg, offset, Operand(0xffffffff)); \ __ And(kScratchReg, offset, Operand(0xffffffff)); \
__ Daddu(kScratchReg, i.InputRegister(3), kScratchReg); \ __ Daddu(kScratchReg, i.InputRegister(3), kScratchReg); \
__ asm_instr(value, MemOperand(kScratchReg, 0)); \ __ asm_instr(value, MemOperand(kScratchReg, 0)); \
} else { \ } else { \
int offset = static_cast<int>(i.InputOperand(0).immediate()); \ int offset = static_cast<int>(i.InputOperand(0).immediate()); \
auto value = i.InputRegister(2); \ auto value = i.InputOrZeroRegister(2); \
__ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \ __ Branch(&done, ls, i.InputRegister(1), Operand(offset)); \
__ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \ __ asm_instr(value, MemOperand(i.InputRegister(3), offset)); \
} \ } \
@ -489,11 +503,11 @@ FPUCondition FlagsConditionToConditionCmpFPU(bool& predicate,
__ sync(); \ __ sync(); \
} while (0) } while (0)
#define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \ #define ASSEMBLE_ATOMIC_STORE_INTEGER(asm_instr) \
do { \ do { \
__ sync(); \ __ sync(); \
__ asm_instr(i.InputRegister(2), i.MemoryOperand()); \ __ asm_instr(i.InputOrZeroRegister(2), i.MemoryOperand()); \
__ sync(); \ __ sync(); \
} while (0) } while (0)
#define ASSEMBLE_IEEE754_BINOP(name) \ #define ASSEMBLE_IEEE754_BINOP(name) \
@ -1683,7 +1697,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ lb(i.OutputRegister(), i.MemoryOperand()); __ lb(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMips64Sb: case kMips64Sb:
__ sb(i.InputRegister(2), i.MemoryOperand()); __ sb(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Lhu: case kMips64Lhu:
__ lhu(i.OutputRegister(), i.MemoryOperand()); __ lhu(i.OutputRegister(), i.MemoryOperand());
@ -1698,10 +1712,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Ulh(i.OutputRegister(), i.MemoryOperand()); __ Ulh(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMips64Sh: case kMips64Sh:
__ sh(i.InputRegister(2), i.MemoryOperand()); __ sh(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Ush: case kMips64Ush:
__ Ush(i.InputRegister(2), i.MemoryOperand(), kScratchReg); __ Ush(i.InputOrZeroRegister(2), i.MemoryOperand(), kScratchReg);
break; break;
case kMips64Lw: case kMips64Lw:
__ lw(i.OutputRegister(), i.MemoryOperand()); __ lw(i.OutputRegister(), i.MemoryOperand());
@ -1722,16 +1736,16 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Uld(i.OutputRegister(), i.MemoryOperand()); __ Uld(i.OutputRegister(), i.MemoryOperand());
break; break;
case kMips64Sw: case kMips64Sw:
__ sw(i.InputRegister(2), i.MemoryOperand()); __ sw(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Usw: case kMips64Usw:
__ Usw(i.InputRegister(2), i.MemoryOperand()); __ Usw(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Sd: case kMips64Sd:
__ sd(i.InputRegister(2), i.MemoryOperand()); __ sd(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Usd: case kMips64Usd:
__ Usd(i.InputRegister(2), i.MemoryOperand()); __ Usd(i.InputOrZeroRegister(2), i.MemoryOperand());
break; break;
case kMips64Lwc1: { case kMips64Lwc1: {
__ lwc1(i.OutputSingleRegister(), i.MemoryOperand()); __ lwc1(i.OutputSingleRegister(), i.MemoryOperand());
@ -1744,13 +1758,21 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kMips64Swc1: { case kMips64Swc1: {
size_t index = 0; size_t index = 0;
MemOperand operand = i.MemoryOperand(&index); MemOperand operand = i.MemoryOperand(&index);
__ swc1(i.InputSingleRegister(index), operand); FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ swc1(ft, operand);
break; break;
} }
case kMips64Uswc1: { case kMips64Uswc1: {
size_t index = 0; size_t index = 0;
MemOperand operand = i.MemoryOperand(&index); MemOperand operand = i.MemoryOperand(&index);
__ Uswc1(i.InputSingleRegister(index), operand, kScratchReg); FPURegister ft = i.InputOrZeroSingleRegister(index);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Uswc1(ft, operand, kScratchReg);
break; break;
} }
case kMips64Ldc1: case kMips64Ldc1:
@ -1759,12 +1781,22 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
case kMips64Uldc1: case kMips64Uldc1:
__ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg); __ Uldc1(i.OutputDoubleRegister(), i.MemoryOperand(), kScratchReg);
break; break;
case kMips64Sdc1: case kMips64Sdc1: {
__ sdc1(i.InputDoubleRegister(2), i.MemoryOperand()); FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ sdc1(ft, i.MemoryOperand());
break; break;
case kMips64Usdc1: }
__ Usdc1(i.InputDoubleRegister(2), i.MemoryOperand(), kScratchReg); case kMips64Usdc1: {
FPURegister ft = i.InputOrZeroDoubleRegister(2);
if (ft.is(kDoubleRegZero) && !__ IsDoubleZeroRegSet()) {
__ Move(kDoubleRegZero, 0.0);
}
__ Usdc1(ft, i.MemoryOperand(), kScratchReg);
break; break;
}
case kMips64Push: case kMips64Push:
if (instr->InputAt(0)->IsFPRegister()) { if (instr->InputAt(0)->IsFPRegister()) {
__ sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize)); __ sdc1(i.InputDoubleRegister(0), MemOperand(sp, -kDoubleSize));

View File

@ -31,6 +31,17 @@ class Mips64OperandGenerator final : public OperandGenerator {
return UseRegister(node); return UseRegister(node);
} }
// Use the zero register if the node has the immediate value zero, otherwise
// assign a register.
InstructionOperand UseRegisterOrImmediateZero(Node* node) {
if ((IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) ||
(IsFloatConstant(node) &&
(bit_cast<int64_t>(GetFloatConstantValue(node)) == V8_INT64_C(0)))) {
return UseImmediate(node);
}
return UseRegister(node);
}
bool IsIntegerConstant(Node* node) { bool IsIntegerConstant(Node* node) {
return (node->opcode() == IrOpcode::kInt32Constant) || return (node->opcode() == IrOpcode::kInt32Constant) ||
(node->opcode() == IrOpcode::kInt64Constant); (node->opcode() == IrOpcode::kInt64Constant);
@ -44,6 +55,19 @@ class Mips64OperandGenerator final : public OperandGenerator {
return OpParameter<int64_t>(node); return OpParameter<int64_t>(node);
} }
bool IsFloatConstant(Node* node) {
return (node->opcode() == IrOpcode::kFloat32Constant) ||
(node->opcode() == IrOpcode::kFloat64Constant);
}
double GetFloatConstantValue(Node* node) {
if (node->opcode() == IrOpcode::kFloat32Constant) {
return OpParameter<float>(node);
}
DCHECK_EQ(IrOpcode::kFloat64Constant, node->opcode());
return OpParameter<double>(node);
}
bool CanBeImmediate(Node* node, InstructionCode mode) { bool CanBeImmediate(Node* node, InstructionCode mode) {
return IsIntegerConstant(node) && return IsIntegerConstant(node) &&
CanBeImmediate(GetIntegerConstantValue(node), mode); CanBeImmediate(GetIntegerConstantValue(node), mode);
@ -401,14 +425,15 @@ void InstructionSelector::VisitStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }
} }
@ -1767,14 +1792,15 @@ void InstructionSelector::VisitUnalignedStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }
@ -1876,7 +1902,7 @@ void InstructionSelector::VisitCheckedStore(Node* node) {
: g.UseRegister(length); : g.UseRegister(length);
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
offset_operand, length_operand, g.UseRegister(value), offset_operand, length_operand, g.UseRegisterOrImmediateZero(value),
g.UseRegister(buffer)); g.UseRegister(buffer));
} }
@ -2519,14 +2545,15 @@ void InstructionSelector::VisitAtomicStore(Node* node) {
if (g.CanBeImmediate(index, opcode)) { if (g.CanBeImmediate(index, opcode)) {
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value)); g.UseRegister(base), g.UseImmediate(index),
g.UseRegisterOrImmediateZero(value));
} else { } else {
InstructionOperand addr_reg = g.TempRegister(); InstructionOperand addr_reg = g.TempRegister();
Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg, Emit(kMips64Dadd | AddressingModeField::encode(kMode_None), addr_reg,
g.UseRegister(index), g.UseRegister(base)); g.UseRegister(index), g.UseRegister(base));
// Emit desired store opcode, using temp addr_reg. // Emit desired store opcode, using temp addr_reg.
Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(), Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
addr_reg, g.TempImmediate(0), g.UseRegister(value)); addr_reg, g.TempImmediate(0), g.UseRegisterOrImmediateZero(value));
} }
} }

View File

@ -49,7 +49,8 @@ class InstructionSelectorTest : public TestWithContext,
test->isolate(), new (test->zone()) Graph(test->zone()), test->isolate(), new (test->zone()) Graph(test->zone()),
MakeCallDescriptor(test->zone(), return_type, parameter0_type), MakeCallDescriptor(test->zone(), return_type, parameter0_type),
MachineType::PointerRepresentation(), MachineType::PointerRepresentation(),
MachineOperatorBuilder::kAllOptionalOps), MachineOperatorBuilder::kAllOptionalOps,
InstructionSelector::AlignmentRequirements()),
test_(test) {} test_(test) {}
StreamBuilder(InstructionSelectorTest* test, MachineType return_type, StreamBuilder(InstructionSelectorTest* test, MachineType return_type,
MachineType parameter0_type, MachineType parameter1_type) MachineType parameter0_type, MachineType parameter1_type)

View File

@ -859,6 +859,18 @@ std::ostream& operator<<(std::ostream& os, const MemoryAccessImm1& acc) {
return os << acc.type; return os << acc.type;
} }
struct MemoryAccessImm2 {
MachineType type;
ArchOpcode store_opcode;
ArchOpcode store_opcode_unaligned;
bool (InstructionSelectorTest::Stream::*val_predicate)(
const InstructionOperand*) const;
const int32_t immediates[40];
};
std::ostream& operator<<(std::ostream& os, const MemoryAccessImm2& acc) {
return os << acc.type;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Loads and stores immediate values. // Loads and stores immediate values.
@ -954,6 +966,40 @@ const MemoryAccessImm1 kMemoryAccessImmMoreThan16bit[] = {
&InstructionSelectorTest::Stream::IsDouble, &InstructionSelectorTest::Stream::IsDouble,
{-65000, -55000, 32777, 55000, 65000}}}; {-65000, -55000, 32777, 55000, 65000}}};
const MemoryAccessImm2 kMemoryAccessesImmUnaligned[] = {
{MachineType::Int16(),
kMipsUsh,
kMipsSh,
&InstructionSelectorTest::Stream::IsInteger,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Int32(),
kMipsUsw,
kMipsSw,
&InstructionSelectorTest::Stream::IsInteger,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Float32(),
kMipsUswc1,
kMipsSwc1,
&InstructionSelectorTest::Stream::IsDouble,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Float64(),
kMipsUsdc1,
kMipsSdc1,
&InstructionSelectorTest::Stream::IsDouble,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}};
} // namespace } // namespace
@ -1043,11 +1089,60 @@ TEST_P(InstructionSelectorMemoryAccessImmTest, StoreWithImmediateIndex) {
} }
} }
TEST_P(InstructionSelectorMemoryAccessImmTest, StoreZero) {
const MemoryAccessImm memacc = GetParam();
TRACED_FOREACH(int32_t, index, memacc.immediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer());
m.Store(memacc.type.representation(), m.Parameter(0),
m.Int32Constant(index), m.Int32Constant(0), kNoWriteBarrier);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind());
EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2)));
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorMemoryAccessImmTest, InstructionSelectorMemoryAccessImmTest,
::testing::ValuesIn(kMemoryAccessesImm)); ::testing::ValuesIn(kMemoryAccessesImm));
typedef InstructionSelectorTestWithParam<MemoryAccessImm2>
InstructionSelectorMemoryAccessUnalignedImmTest;
TEST_P(InstructionSelectorMemoryAccessUnalignedImmTest, StoreZero) {
const MemoryAccessImm2 memacc = GetParam();
TRACED_FOREACH(int32_t, index, memacc.immediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer());
bool unaligned_store_supported = m.machine()->UnalignedStoreSupported(
MachineType::TypeForRepresentation(memacc.type.representation()), 1);
m.UnalignedStore(memacc.type.representation(), m.Parameter(0),
m.Int32Constant(index), m.Int32Constant(0));
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(unaligned_store_supported ? memacc.store_opcode_unaligned
: memacc.store_opcode,
s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind());
EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2)));
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorMemoryAccessUnalignedImmTest,
::testing::ValuesIn(kMemoryAccessesImmUnaligned));
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Load/store offsets more than 16 bits. // Load/store offsets more than 16 bits.

View File

@ -1284,6 +1284,18 @@ std::ostream& operator<<(std::ostream& os, const MemoryAccessImm1& acc) {
return os << acc.type; return os << acc.type;
} }
struct MemoryAccessImm2 {
MachineType type;
ArchOpcode store_opcode;
ArchOpcode store_opcode_unaligned;
bool (InstructionSelectorTest::Stream::*val_predicate)(
const InstructionOperand*) const;
const int32_t immediates[40];
};
std::ostream& operator<<(std::ostream& os, const MemoryAccessImm2& acc) {
return os << acc.type;
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Loads and stores immediate values // Loads and stores immediate values
@ -1391,6 +1403,48 @@ const MemoryAccessImm1 kMemoryAccessImmMoreThan16bit[] = {
&InstructionSelectorTest::Stream::IsInteger, &InstructionSelectorTest::Stream::IsInteger,
{-65000, -55000, 32777, 55000, 65000}}}; {-65000, -55000, 32777, 55000, 65000}}};
const MemoryAccessImm2 kMemoryAccessesImmUnaligned[] = {
{MachineType::Int16(),
kMips64Ush,
kMips64Sh,
&InstructionSelectorTest::Stream::IsInteger,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Int32(),
kMips64Usw,
kMips64Sw,
&InstructionSelectorTest::Stream::IsInteger,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Int64(),
kMips64Usd,
kMips64Sd,
&InstructionSelectorTest::Stream::IsInteger,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Float32(),
kMips64Uswc1,
kMips64Swc1,
&InstructionSelectorTest::Stream::IsDouble,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}},
{MachineType::Float64(),
kMips64Usdc1,
kMips64Sdc1,
&InstructionSelectorTest::Stream::IsDouble,
{-4095, -3340, -3231, -3224, -3088, -1758, -1203, -123, -117, -91,
-89, -87, -86, -82, -44, -23, -3, 0, 7, 10,
39, 52, 69, 71, 91, 92, 107, 109, 115, 124,
286, 655, 1362, 1569, 2587, 3067, 3096, 3462, 3510, 4095}}};
} // namespace } // namespace
@ -1477,10 +1531,60 @@ TEST_P(InstructionSelectorMemoryAccessImmTest, StoreWithImmediateIndex) {
} }
} }
TEST_P(InstructionSelectorMemoryAccessImmTest, StoreZero) {
const MemoryAccessImm memacc = GetParam();
TRACED_FOREACH(int32_t, index, memacc.immediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer());
m.Store(memacc.type.representation(), m.Parameter(0),
m.Int32Constant(index), m.Int32Constant(0), kNoWriteBarrier);
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(memacc.store_opcode, s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind());
EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2)));
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest, INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorMemoryAccessImmTest, InstructionSelectorMemoryAccessImmTest,
::testing::ValuesIn(kMemoryAccessesImm)); ::testing::ValuesIn(kMemoryAccessesImm));
typedef InstructionSelectorTestWithParam<MemoryAccessImm2>
InstructionSelectorMemoryAccessUnalignedImmTest;
TEST_P(InstructionSelectorMemoryAccessUnalignedImmTest, StoreZero) {
const MemoryAccessImm2 memacc = GetParam();
TRACED_FOREACH(int32_t, index, memacc.immediates) {
StreamBuilder m(this, MachineType::Int32(), MachineType::Pointer());
bool unaligned_store_supported = m.machine()->UnalignedStoreSupported(
MachineType::TypeForRepresentation(memacc.type.representation()), 1);
m.UnalignedStore(memacc.type.representation(), m.Parameter(0),
m.Int32Constant(index), m.Int32Constant(0));
m.Return(m.Int32Constant(0));
Stream s = m.Build();
ASSERT_EQ(1U, s.size());
EXPECT_EQ(unaligned_store_supported ? memacc.store_opcode_unaligned
: memacc.store_opcode,
s[0]->arch_opcode());
EXPECT_EQ(kMode_MRI, s[0]->addressing_mode());
ASSERT_EQ(3U, s[0]->InputCount());
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(1)->kind());
EXPECT_EQ(index, s.ToInt32(s[0]->InputAt(1)));
ASSERT_EQ(InstructionOperand::IMMEDIATE, s[0]->InputAt(2)->kind());
EXPECT_EQ(0, s.ToInt64(s[0]->InputAt(2)));
EXPECT_EQ(0U, s[0]->OutputCount());
}
}
INSTANTIATE_TEST_CASE_P(InstructionSelectorTest,
InstructionSelectorMemoryAccessUnalignedImmTest,
::testing::ValuesIn(kMemoryAccessesImmUnaligned));
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Load/store offsets more than 16 bits. // Load/store offsets more than 16 bits.