Faster 64-bit right-shift for the ia32 compiler.

Review URL: https://codereview.chromium.org//11027060

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@13295 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
fschneider@google.com 2012-10-05 14:42:00 +00:00
parent 980ffae497
commit 3efefbd582
12 changed files with 254 additions and 66 deletions

View file

@ -1220,7 +1220,7 @@ void Assembler::shll(Register reg, const Immediate& imm) {
void Assembler::shll(Register operand, Register shifter) {
EmitGenericShift(4, operand, shifter);
EmitGenericShift(4, Operand(operand), shifter);
}
@ -1230,7 +1230,7 @@ void Assembler::shrl(Register reg, const Immediate& imm) {
void Assembler::shrl(Register operand, Register shifter) {
EmitGenericShift(5, operand, shifter);
EmitGenericShift(5, Operand(operand), shifter);
}
@ -1240,7 +1240,12 @@ void Assembler::sarl(Register reg, const Immediate& imm) {
void Assembler::sarl(Register operand, Register shifter) {
EmitGenericShift(7, operand, shifter);
EmitGenericShift(7, Operand(operand), shifter);
}
void Assembler::sarl(const Address& address, Register shifter) {
EmitGenericShift(7, Operand(address), shifter);
}
@ -1252,6 +1257,22 @@ void Assembler::shld(Register dst, Register src) {
}
void Assembler::shrd(Register dst, Register src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
EmitUint8(0xAD);
EmitRegisterOperand(src, dst);
}
void Assembler::shrd(const Address& dst, Register src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x0F);
EmitUint8(0xAD);
EmitOperand(src, Operand(dst));
}
void Assembler::negl(Register reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF7);
@ -1906,7 +1927,7 @@ void Assembler::EmitGenericShift(int rm,
void Assembler::EmitGenericShift(int rm,
Register operand,
const Operand& operand,
Register shifter) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
ASSERT(shifter == ECX);

View file

@ -490,7 +490,10 @@ class Assembler : public ValueObject {
void shrl(Register operand, Register shifter);
void sarl(Register reg, const Immediate& imm);
void sarl(Register operand, Register shifter);
void sarl(const Address& address, Register shifter);
void shld(Register dst, Register src);
void shrd(Register dst, Register src);
void shrd(const Address& dst, Register src);
void negl(Register reg);
void notl(Register reg);
@ -657,7 +660,7 @@ class Assembler : public ValueObject {
void EmitNearLabelLink(Label* label);
void EmitGenericShift(int rm, Register reg, const Immediate& imm);
void EmitGenericShift(int rm, Register operand, Register shifter);
void EmitGenericShift(int rm, const Operand& operand, Register shifter);
void StoreIntoObjectFilter(Register object, Register value, Label* no_update);

View file

@ -494,6 +494,19 @@ ASSEMBLER_TEST_GENERATE(LogicalOps, assembler) {
__ movl(Address(EAX, 0), EAX);
__ Bind(&donetest13);
Label donetest14;
__ subl(ESP, Immediate(kWordSize));
__ movl(Address(ESP, 0), Immediate(0x80000000));
__ movl(EAX, Immediate(0));
__ movl(ECX, Immediate(3));
__ sarl(Address(ESP, 0), ECX);
__ shrd(Address(ESP, 0), EAX);
__ cmpl(Address(ESP, 0), Immediate(0x1e000000));
__ j(EQUAL, &donetest14);
__ int3();
__ Bind(&donetest14);
__ addl(ESP, Immediate(kWordSize));
__ movl(EAX, Immediate(0));
__ ret();
}

View file

@ -63,6 +63,7 @@ DECLARE_RUNTIME_ENTRY(UpdateICDataTwoArgs);
V(DoubleToDouble) \
V(BinarySmiOp) \
V(BinaryMintOp) \
V(ShiftMintOp) \
V(BinaryDoubleOp) \
V(InstanceSetterSameTarget) \
V(InstanceSetter) \

View file

@ -760,52 +760,33 @@ int X86Decoder::CMov(uint8_t* data) {
int X86Decoder::D1D3C1Instruction(uint8_t* data) {
uint8_t op = *data;
ASSERT(op == 0xD1 || op == 0xD3 || op == 0xC1);
uint8_t modrm = *(data+1);
int mod, regop, rm;
GetModRm(modrm, &mod, &regop, &rm);
int imm8 = -1;
int num_bytes = 2;
if (mod == 3) {
const char* mnem = NULL;
if (op == 0xD1) {
imm8 = 1;
switch (regop) {
case edx: mnem = "rcl"; break;
case edi: mnem = "sar"; break;
case esp: mnem = "shl"; break;
case ebp: mnem = "shr"; break;
default: UNIMPLEMENTED();
}
} else if (op == 0xC1) {
imm8 = *(data+2);
num_bytes = 3;
switch (regop) {
case edx: mnem = "rcl"; break;
case esp: mnem = "shl"; break;
case ebp: mnem = "shr"; break;
case edi: mnem = "sar"; break;
default: UNIMPLEMENTED();
}
} else if (op == 0xD3) {
switch (regop) {
case esp: mnem = "shl"; break;
case ebp: mnem = "shr"; break;
case edi: mnem = "sar"; break;
default: UNIMPLEMENTED();
}
}
ASSERT(mnem != NULL);
Print(mnem);
Print(" ");
PrintCPURegister(rm);
Print(",");
if (imm8 > 0) {
PrintInt(imm8);
} else {
Print("cl");
}
GetModRm(*(data+1), &mod, &regop, &rm);
int num_bytes = 1;
const char* mnem = NULL;
switch (regop) {
case 2: mnem = "rcl"; break;
case 4: mnem = "shl"; break;
case 5: mnem = "shr"; break;
case 7: mnem = "sar"; break;
default: UNIMPLEMENTED();
}
ASSERT(mnem != NULL);
Print(mnem);
Print(" ");
if (op == 0xD1) {
num_bytes += PrintRightOperand(data+1);
Print(", 1");
} else if (op == 0xC1) {
num_bytes += PrintRightOperand(data+1);
Print(", ");
PrintInt(*(data+2));
num_bytes++;
} else {
UNIMPLEMENTED();
ASSERT(op == 0xD3);
num_bytes += PrintRightOperand(data+1);
Print(", cl");
}
return num_bytes;
}
@ -1336,6 +1317,7 @@ int X86Decoder::InstructionDecode(uword pc) {
Print(f0mnem);
int mod, regop, rm;
GetModRm(*data, &mod, &regop, &rm);
Print(" ");
data += PrintRightOperand(data);
if (f0byte == 0xAB) {
Print(",");

View file

@ -148,7 +148,8 @@ void FlowGraphOptimizer::SelectRepresentations() {
if (join_entry->phis() != NULL) {
for (intptr_t i = 0; i < join_entry->phis()->length(); ++i) {
PhiInstr* phi = (*join_entry->phis())[i];
if ((phi != NULL) && (phi->GetPropagatedCid() == kDoubleCid)) {
if (phi == NULL) continue;
if (phi->GetPropagatedCid() == kDoubleCid) {
phi->set_representation(kUnboxedDouble);
}
}
@ -268,7 +269,7 @@ static bool HasOnlySmiOrMint(const ICData& ic_data) {
}
static bool HasOnlyTwoSmi(const ICData& ic_data) {
static bool HasOnlyTwoSmis(const ICData& ic_data) {
return (ic_data.NumberOfChecks() == 1) &&
ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid);
}
@ -474,7 +475,7 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
switch (op_kind) {
case Token::kADD:
case Token::kSUB:
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else if (HasTwoMintOrSmi(ic_data) &&
FlowGraphCompiler::SupportsUnboxedMints()) {
@ -486,7 +487,7 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
}
break;
case Token::kMUL:
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else if (ShouldSpecializeForDouble(ic_data)) {
operands_type = kDoubleCid;
@ -502,7 +503,7 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
}
break;
case Token::kMOD:
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else {
return false;
@ -511,7 +512,17 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
case Token::kBIT_AND:
case Token::kBIT_OR:
case Token::kBIT_XOR:
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else if (HasTwoMintOrSmi(ic_data) &&
FlowGraphCompiler::SupportsUnboxedMints()) {
operands_type = kMintCid;
} else {
return false;
}
break;
case Token::kSHR:
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else if (HasTwoMintOrSmi(ic_data) &&
FlowGraphCompiler::SupportsUnboxedMints()) {
@ -521,9 +532,8 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
}
break;
case Token::kTRUNCDIV:
case Token::kSHR:
case Token::kSHL:
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
operands_type = kSmiCid;
} else {
return false;
@ -554,9 +564,15 @@ bool FlowGraphOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call,
} else if (operands_type == kMintCid) {
Value* left = call->ArgumentAt(0)->value();
Value* right = call->ArgumentAt(1)->value();
BinaryMintOpInstr* bin_op =
new BinaryMintOpInstr(op_kind, left, right, call);
call->ReplaceWith(bin_op, current_iterator());
if (op_kind == Token::kSHR) {
ShiftMintOpInstr* shift_op =
new ShiftMintOpInstr(op_kind, left, right, call);
call->ReplaceWith(shift_op, current_iterator());
} else {
BinaryMintOpInstr* bin_op =
new BinaryMintOpInstr(op_kind, left, right, call);
call->ReplaceWith(bin_op, current_iterator());
}
RemovePushArguments(call);
} else if (op_kind == Token::kMOD) {
// TODO(vegorov): implement fast path code for modulo.
@ -1042,7 +1058,7 @@ static void HandleRelationalOp(FlowGraphOptimizer* optimizer,
if (ic_data.NumberOfChecks() != 1) return;
ASSERT(ic_data.HasOneTarget());
if (HasOnlyTwoSmi(ic_data)) {
if (HasOnlyTwoSmis(ic_data)) {
optimizer->InsertBefore(
instr,
new CheckSmiInstr(comp->left()->Copy(), comp->deopt_id()),
@ -2983,6 +2999,13 @@ void ConstantPropagator::VisitBinaryMintOp(
}
void ConstantPropagator::VisitShiftMintOp(
ShiftMintOpInstr* instr) {
// TODO(kmillikin): Handle shift operations.
SetValue(instr, non_constant_);
}
void ConstantPropagator::VisitUnaryMintOp(
UnaryMintOpInstr* instr) {
// TODO(kmillikin): Handle unary operations.

View file

@ -510,6 +510,14 @@ void BinaryMintOpInstr::PrintOperandsTo(BufferFormatter* f) const {
}
void ShiftMintOpInstr::PrintOperandsTo(BufferFormatter* f) const {
f->Print("%s, ", Token::Str(op_kind()));
left()->PrintTo(f);
f->Print(", ");
right()->PrintTo(f);
}
void UnaryMintOpInstr::PrintOperandsTo(BufferFormatter* f) const {
f->Print("%s, ", Token::Str(op_kind()));
value()->PrintTo(f);

View file

@ -1212,6 +1212,16 @@ intptr_t BinaryMintOpInstr::ResultCid() const {
}
RawAbstractType* ShiftMintOpInstr::CompileType() const {
return Type::IntType();
}
intptr_t ShiftMintOpInstr::ResultCid() const {
return kDynamicCid;
}
RawAbstractType* UnaryMintOpInstr::CompileType() const {
return Type::IntType();
}

View file

@ -259,6 +259,7 @@ class EmbeddedArray<T, 0> {
M(UnboxInteger) \
M(BoxInteger) \
M(BinaryMintOp) \
M(ShiftMintOp) \
M(UnaryMintOp) \
M(CheckArrayBound) \
M(Constraint) \
@ -516,6 +517,7 @@ FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
friend class UnboxDoubleInstr;
friend class BinaryDoubleOpInstr;
friend class BinaryMintOpInstr;
friend class ShiftMintOpInstr;
friend class UnaryMintOpInstr;
friend class MathSqrtInstr;
friend class CheckClassInstr;
@ -3584,6 +3586,65 @@ class BinaryMintOpInstr : public TemplateDefinition<2> {
};
class ShiftMintOpInstr : public TemplateDefinition<2> {
public:
ShiftMintOpInstr(Token::Kind op_kind,
Value* left,
Value* right,
InstanceCallInstr* instance_call)
: op_kind_(op_kind) {
ASSERT(left != NULL);
ASSERT(right != NULL);
ASSERT(op_kind == Token::kSHR);
inputs_[0] = left;
inputs_[1] = right;
deopt_id_ = instance_call->deopt_id();
}
Value* left() const { return inputs_[0]; }
Value* right() const { return inputs_[1]; }
Token::Kind op_kind() const { return op_kind_; }
virtual void PrintOperandsTo(BufferFormatter* f) const;
virtual bool CanDeoptimize() const { return true; }
virtual bool HasSideEffect() const { return false; }
virtual bool AffectedBySideEffect() const { return false; }
virtual bool AttributesEqual(Instruction* other) const {
return op_kind() == other->AsShiftMintOp()->op_kind();
}
virtual intptr_t ResultCid() const;
virtual RawAbstractType* CompileType() const;
virtual Representation representation() const {
return kUnboxedMint;
}
virtual Representation RequiredInputRepresentation(intptr_t idx) const {
ASSERT((idx == 0) || (idx == 1));
return (idx == 0) ? kUnboxedMint : kTagged;
}
virtual intptr_t DeoptimizationTarget() const {
// Direct access since this instuction cannot deoptimize, and the deopt-id
// was inherited from another instuction that could deoptimize.
return deopt_id_;
}
DECLARE_INSTRUCTION(ShiftMintOp)
private:
const Token::Kind op_kind_;
DISALLOW_COPY_AND_ASSIGN(ShiftMintOpInstr);
};
class UnaryMintOpInstr : public TemplateDefinition<1> {
public:
UnaryMintOpInstr(Token::Kind op_kind,

View file

@ -2373,10 +2373,10 @@ void BinaryMintOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
__ movq(Address(ESP, 0), left);
if (op_kind() == Token::kADD) {
__ addl(Address(ESP, 0), EAX);
__ adcl(Address(ESP, 4), EDX);
__ adcl(Address(ESP, 1 * kWordSize), EDX);
} else {
__ subl(Address(ESP, 0), EAX);
__ sbbl(Address(ESP, 4), EDX);
__ sbbl(Address(ESP, 1 * kWordSize), EDX);
}
__ j(OVERFLOW, &overflow);
__ movq(left, Address(ESP, 0));
@ -2393,6 +2393,56 @@ void BinaryMintOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
LocationSummary* ShiftMintOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 2;
const intptr_t kNumTemps = 1;
LocationSummary* summary =
new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresXmmRegister());
summary->set_in(1, Location::RegisterLocation(ECX));
summary->set_temp(0, Location::RequiresRegister());
summary->set_out(Location::SameAsFirstInput());
return summary;
}
void ShiftMintOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
XmmRegister left = locs()->in(0).xmm_reg();
Register temp = locs()->temp(0).reg();
ASSERT(locs()->in(1).reg() == ECX);
ASSERT(locs()->out().xmm_reg() == left);
switch (op_kind()) {
case Token::kSHR: {
Label* deopt = compiler->AddDeoptStub(deopt_id(),
kDeoptShiftMintOp);
__ subl(ESP, Immediate(2 * kWordSize));
__ movq(Address(ESP, 0), left);
// Deoptimize if shift count is > 31.
// sarl operation masks the count to 5 bits and
// shrd is undefined with count > operand size (32)
// TODO(fschneider): Support shift counts > 31 without deoptimization.
__ SmiUntag(ECX);
const Immediate kCountLimit = Immediate(31);
__ cmpl(ECX, kCountLimit);
__ j(ABOVE, deopt);
__ movl(temp, Address(ESP, 1 * kWordSize));
__ shrd(Address(ESP, 0), temp); // Shift count in CL.
__ sarl(Address(ESP, 1 * kWordSize), ECX); // Shift count in CL.
__ movq(left, Address(ESP, 0));
__ addl(ESP, Immediate(2 * kWordSize));
break;
}
case Token::kSHL:
UNIMPLEMENTED();
break;
default:
UNREACHABLE();
break;
}
}
LocationSummary* UnaryMintOpInstr::MakeLocationSummary() const {
const intptr_t kNumInputs = 1;
const intptr_t kNumTemps = 0;

View file

@ -2198,6 +2198,16 @@ void UnaryMintOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
}
LocationSummary* ShiftMintOpInstr::MakeLocationSummary() const {
UNIMPLEMENTED();
return NULL;
}
void ShiftMintOpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
UNIMPLEMENTED();
}
} // namespace dart
#undef __

View file

@ -54,12 +54,13 @@ class BitOperationsTest {
TestPositiveValueShifts();
TestNoMaskingOfShiftCount();
TestNegativeCountShifts();
for (int i = 0; i < 1000; i++) {
TestCornerCasesLeftShifts();
for (int i = 0; i < 10000; i++) {
TestCornerCasesRightShifts();
TestRightShift64Bit();
}
}
static void TestCornerCasesLeftShifts() {
static void TestCornerCasesRightShifts() {
var v32 = 0xFF000000;
var v64 = 0xFF00000000000000;
Expect.equals(0x3, v32 >> 0x1E);
@ -70,6 +71,11 @@ class BitOperationsTest {
Expect.equals(0x0, v64 >> 0x40);
}
static void TestRightShift64Bit() {
var t = 0x1ffffffff;
Expect.equals(0xffffffff, t >> 1);
}
static void TestNegativeCountShifts() {
bool throwOnLeft(a, b) {
try {