[vm, compiler] Use immediate forms for BinaryUint32Op.

dart2js.aot.arm64  28757440 -> 28691320 (-66k)
dart2js.aot.x64    28311232 -> 28310536 (-1k)
dart2js.aot.rv64   27971912 -> 27955200 (-16k)
dart2js.aot.arm32  27793504 -> 27775936 (-17k)

TEST=vm/dart/uint32_op_imm_test
Change-Id: I25e486ea1ab3b20369c0c5a5fafae5d1ea53b1c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/388120
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak
2024-10-09 16:47:48 +00:00
committed by Commit Queue
parent 950f08f904
commit 889e5795bb
8 changed files with 527 additions and 123 deletions
@@ -0,0 +1,283 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:typed_data";
@pragma("vm:never-inline")
void and1(Uint32List list) {
list[0] &= 1;
}
@pragma("vm:never-inline")
void or1(Uint32List list) {
list[0] |= 1;
}
@pragma("vm:never-inline")
void xor1(Uint32List list) {
list[0] ^= 1;
}
@pragma("vm:never-inline")
void add1(Uint32List list) {
list[0] += 1;
}
@pragma("vm:never-inline")
void sub1(Uint32List list) {
list[0] -= 1;
}
@pragma("vm:never-inline")
void mul1(Uint32List list) {
list[0] *= 1;
}
@pragma("vm:never-inline")
void and2(Uint32List list) {
list[0] &= 2;
}
@pragma("vm:never-inline")
void or2(Uint32List list) {
list[0] |= 2;
}
@pragma("vm:never-inline")
void xor2(Uint32List list) {
list[0] ^= 2;
}
@pragma("vm:never-inline")
void add2(Uint32List list) {
list[0] += 2;
}
@pragma("vm:never-inline")
void sub2(Uint32List list) {
list[0] -= 2;
}
@pragma("vm:never-inline")
void mul2(Uint32List list) {
list[0] *= 2;
}
@pragma("vm:never-inline")
void and7(Uint32List list) {
list[0] &= 7;
}
@pragma("vm:never-inline")
void or7(Uint32List list) {
list[0] |= 7;
}
@pragma("vm:never-inline")
void xor7(Uint32List list) {
list[0] ^= 7;
}
@pragma("vm:never-inline")
void add7(Uint32List list) {
list[0] += 7;
}
@pragma("vm:never-inline")
void sub7(Uint32List list) {
list[0] -= 7;
}
@pragma("vm:never-inline")
void mul7(Uint32List list) {
list[0] *= 7;
}
@pragma("vm:never-inline")
void andH(Uint32List list) {
list[0] &= 0x7FFFFFF;
}
@pragma("vm:never-inline")
void orH(Uint32List list) {
list[0] |= 0x7FFFFFF;
}
@pragma("vm:never-inline")
void xorH(Uint32List list) {
list[0] ^= 0x7FFFFFF;
}
@pragma("vm:never-inline")
void addH(Uint32List list) {
list[0] += 0x7FFFFFF;
}
@pragma("vm:never-inline")
void subH(Uint32List list) {
list[0] -= 0x7FFFFFF;
}
@pragma("vm:never-inline")
void mulH(Uint32List list) {
list[0] *= 0x7FFFFFF;
}
expect(int observed, int expected) {
if (observed != expected) {
throw "0x${observed.toRadixString(16)}";
}
}
main() {
Uint32List u32 = Uint32List(1);
u32[0] = 0x12345678;
and1(u32);
expect(u32[0], 0);
u32[0] = 0x87654321;
and1(u32);
expect(u32[0], 1);
u32[0] = 0x12345678;
or1(u32);
expect(u32[0], 0x12345679);
u32[0] = 0x87654321;
or1(u32);
expect(u32[0], 0x87654321);
u32[0] = 0x12345678;
xor1(u32);
expect(u32[0], 0x12345679);
u32[0] = 0x87654321;
xor1(u32);
expect(u32[0], 0x87654320);
u32[0] = 0x12345678;
add1(u32);
expect(u32[0], 0x12345679);
u32[0] = 0x87654321;
add1(u32);
expect(u32[0], 0x87654322);
u32[0] = 0x12345678;
sub1(u32);
expect(u32[0], 0x12345677);
u32[0] = 0x87654321;
sub1(u32);
expect(u32[0], 0x87654320);
u32[0] = 0x12345678;
mul1(u32);
expect(u32[0], 0x12345678);
u32[0] = 0x87654321;
mul1(u32);
expect(u32[0], 0x87654321);
u32[0] = 0x12345678;
and2(u32);
expect(u32[0], 0);
u32[0] = 0x87654321;
and2(u32);
expect(u32[0], 0);
u32[0] = 0x12345678;
or2(u32);
expect(u32[0], 0x1234567a);
u32[0] = 0x87654321;
or2(u32);
expect(u32[0], 0x87654323);
u32[0] = 0x12345678;
xor2(u32);
expect(u32[0], 0x1234567a);
u32[0] = 0x87654321;
xor2(u32);
expect(u32[0], 0x87654323);
u32[0] = 0x12345678;
add2(u32);
expect(u32[0], 0x1234567a);
u32[0] = 0x87654321;
add2(u32);
expect(u32[0], 0x87654323);
u32[0] = 0x12345678;
sub2(u32);
expect(u32[0], 0x12345676);
u32[0] = 0x87654321;
sub2(u32);
expect(u32[0], 0x8765431f);
u32[0] = 0x12345678;
mul2(u32);
expect(u32[0], 0x2468acf0);
u32[0] = 0x87654321;
mul2(u32);
expect(u32[0], 0x0eca8642);
u32[0] = 0x12345678;
and7(u32);
expect(u32[0], 0);
u32[0] = 0x87654321;
and7(u32);
expect(u32[0], 1);
u32[0] = 0x12345678;
or7(u32);
expect(u32[0], 0x1234567f);
u32[0] = 0x87654321;
or7(u32);
expect(u32[0], 0x87654327);
u32[0] = 0x12345678;
xor7(u32);
expect(u32[0], 0x1234567f);
u32[0] = 0x87654321;
xor7(u32);
expect(u32[0], 0x87654326);
u32[0] = 0x12345678;
add7(u32);
expect(u32[0], 0x1234567f);
u32[0] = 0x87654321;
add7(u32);
expect(u32[0], 0x87654328);
u32[0] = 0x12345678;
sub7(u32);
expect(u32[0], 0x12345671);
u32[0] = 0x87654321;
sub7(u32);
expect(u32[0], 0x8765431a);
u32[0] = 0x12345678;
mul7(u32);
expect(u32[0], 0x7f6e5d48);
u32[0] = 0x87654321;
mul7(u32);
expect(u32[0], 0xb3c4d5e7);
u32[0] = 0x12345678;
andH(u32);
expect(u32[0], 0x02345678);
u32[0] = 0x87654321;
andH(u32);
expect(u32[0], 0x07654321);
u32[0] = 0x12345678;
orH(u32);
expect(u32[0], 0x17ffffff);
u32[0] = 0x87654321;
orH(u32);
expect(u32[0], 0x87ffffff);
u32[0] = 0x12345678;
xorH(u32);
expect(u32[0], 0x15cba987);
u32[0] = 0x87654321;
xorH(u32);
expect(u32[0], 0x809abcde);
u32[0] = 0x12345678;
addH(u32);
expect(u32[0], 0x1a345677);
u32[0] = 0x87654321;
addH(u32);
expect(u32[0], 0x8f654320);
u32[0] = 0x12345678;
subH(u32);
expect(u32[0], 0x0a345679);
u32[0] = 0x87654321;
subH(u32);
expect(u32[0], 0x7f654322);
u32[0] = 0x12345678;
mulH(u32);
expect(u32[0], 0xadcba988);
u32[0] = 0x87654321;
mulH(u32);
expect(u32[0], 0x809abcdf);
}
@@ -3107,6 +3107,19 @@ void Assembler::OrImmediate(Register rd,
}
}
void Assembler::XorImmediate(Register rd,
Register rs,
int32_t imm,
Condition cond) {
Operand o;
if (Operand::CanHold(imm, &o)) {
eor(rd, rs, Operand(o), cond);
} else {
LoadImmediate(TMP, imm, cond);
eor(rd, rs, Operand(TMP), cond);
}
}
void Assembler::CompareImmediate(Register rn, int32_t value, Condition cond) {
Operand o;
if (Operand::CanHold(value, &o)) {
@@ -863,12 +863,18 @@ class Assembler : public AssemblerBase {
void MulImmediate(Register reg,
int32_t imm,
OperandSize width = kFourBytes) override {
MulImmediate(reg, reg, imm, width);
}
void MulImmediate(Register rd,
Register rn,
int32_t imm,
OperandSize width = kFourBytes) {
ASSERT(width == kFourBytes);
if (Utils::IsPowerOfTwo(imm)) {
LslImmediate(reg, Utils::ShiftForPowerOfTwo(imm));
LslImmediate(rd, rn, Utils::ShiftForPowerOfTwo(imm));
} else {
LoadImmediate(TMP, imm);
mul(reg, reg, TMP);
mul(rd, rn, TMP);
}
}
void AndImmediate(Register rd,
@@ -913,6 +919,7 @@ class Assembler : public AssemblerBase {
void OrImmediate(Register rd, int32_t imm, Condition cond = AL) {
OrImmediate(rd, rd, imm, cond);
}
void XorImmediate(Register rd, Register rn, int32_t imm, Condition cond = AL);
void LslImmediate(Register rd,
Register rn,
int32_t shift,
@@ -1805,15 +1805,21 @@ class Assembler : public AssemblerBase {
void MulImmediate(Register reg,
int64_t imm,
OperandSize width = kEightBytes) override {
MulImmediate(reg, reg, imm, width);
}
void MulImmediate(Register dest,
Register rn,
int64_t imm,
OperandSize width = kEightBytes) {
ASSERT(width == kFourBytes || width == kEightBytes);
if (Utils::IsPowerOfTwo(imm)) {
LslImmediate(reg, Utils::ShiftForPowerOfTwo(imm), width);
LslImmediate(dest, rn, Utils::ShiftForPowerOfTwo(imm), width);
} else {
LoadImmediate(TMP, imm);
if (width == kFourBytes) {
mulw(reg, reg, TMP);
mulw(dest, rn, TMP);
} else {
mul(reg, reg, TMP);
mul(dest, rn, TMP);
}
}
}
+52 -24
View File
@@ -6967,37 +6967,65 @@ LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrConstant(right()));
summary->set_out(0, Location::RequiresRegister());
return summary;
}
void BinaryUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register left = locs()->in(0).reg();
Register right = locs()->in(1).reg();
Register out = locs()->out(0).reg();
Register left = locs()->in(0).reg();
ASSERT(out != left);
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, compiler::Operand(right));
break;
case Token::kBIT_OR:
__ orr(out, left, compiler::Operand(right));
break;
case Token::kBIT_XOR:
__ eor(out, left, compiler::Operand(right));
break;
case Token::kADD:
__ add(out, left, compiler::Operand(right));
break;
case Token::kSUB:
__ sub(out, left, compiler::Operand(right));
break;
case Token::kMUL:
__ mul(out, left, right);
break;
default:
UNREACHABLE();
if (locs()->in(1).IsConstant()) {
int64_t right;
const bool ok = compiler::HasIntegerValue(locs()->in(1).constant(), &right);
RELEASE_ASSERT(ok);
switch (op_kind()) {
case Token::kBIT_AND:
__ AndImmediate(out, left, right);
break;
case Token::kBIT_OR:
__ OrImmediate(out, left, right);
break;
case Token::kBIT_XOR:
__ XorImmediate(out, left, right);
break;
case Token::kADD:
__ AddImmediate(out, left, right);
break;
case Token::kSUB:
__ AddImmediate(out, left, -right);
break;
case Token::kMUL:
__ MulImmediate(out, left, right);
break;
default:
UNREACHABLE();
}
} else {
Register right = locs()->in(1).reg();
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, compiler::Operand(right));
break;
case Token::kBIT_OR:
__ orr(out, left, compiler::Operand(right));
break;
case Token::kBIT_XOR:
__ eor(out, left, compiler::Operand(right));
break;
case Token::kADD:
__ add(out, left, compiler::Operand(right));
break;
case Token::kSUB:
__ sub(out, left, compiler::Operand(right));
break;
case Token::kMUL:
__ mul(out, left, right);
break;
default:
UNREACHABLE();
}
}
}
+53 -25
View File
@@ -6019,37 +6019,65 @@ LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrConstant(right()));
summary->set_out(0, Location::RequiresRegister());
return summary;
}
void BinaryUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register left = locs()->in(0).reg();
Register right = locs()->in(1).reg();
compiler::Operand r = compiler::Operand(right);
Register out = locs()->out(0).reg();
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, r);
break;
case Token::kBIT_OR:
__ orr(out, left, r);
break;
case Token::kBIT_XOR:
__ eor(out, left, r);
break;
case Token::kADD:
__ addw(out, left, r);
break;
case Token::kSUB:
__ subw(out, left, r);
break;
case Token::kMUL:
__ mulw(out, left, right);
break;
default:
UNREACHABLE();
Register left = locs()->in(0).reg();
if (locs()->in(1).IsConstant()) {
int64_t right;
const bool ok = compiler::HasIntegerValue(locs()->in(1).constant(), &right);
RELEASE_ASSERT(ok);
switch (op_kind()) {
case Token::kBIT_AND:
__ AndImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kBIT_OR:
__ OrImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kBIT_XOR:
__ XorImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kADD:
__ AddImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kSUB:
__ AddImmediate(out, left, -right, compiler::kFourBytes);
break;
case Token::kMUL:
__ MulImmediate(out, left, right, compiler::kFourBytes);
break;
default:
UNREACHABLE();
}
} else {
Register right = locs()->in(1).reg();
compiler::Operand r = compiler::Operand(right);
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, r);
break;
case Token::kBIT_OR:
__ orr(out, left, r);
break;
case Token::kBIT_XOR:
__ eor(out, left, r);
break;
case Token::kADD:
__ addw(out, left, r);
break;
case Token::kSUB:
__ subw(out, left, r);
break;
case Token::kMUL:
__ mulw(out, left, right);
break;
default:
UNREACHABLE();
}
}
}
+54 -26
View File
@@ -6576,44 +6576,72 @@ LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrConstant(right()));
summary->set_out(0, Location::RequiresRegister());
return summary;
}
void BinaryUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register left = locs()->in(0).reg();
Register right = locs()->in(1).reg();
Register out = locs()->out(0).reg();
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, right);
break;
case Token::kBIT_OR:
__ or_(out, left, right);
break;
case Token::kBIT_XOR:
__ xor_(out, left, right);
break;
case Token::kADD:
Register left = locs()->in(0).reg();
if (locs()->in(1).IsConstant()) {
int64_t right;
const bool ok = compiler::HasIntegerValue(locs()->in(1).constant(), &right);
RELEASE_ASSERT(ok);
switch (op_kind()) {
case Token::kBIT_AND:
__ AndImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kBIT_OR:
__ OrImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kBIT_XOR:
__ XorImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kADD:
__ AddImmediate(out, left, right, compiler::kFourBytes);
break;
case Token::kSUB:
__ AddImmediate(out, left, -right, compiler::kFourBytes);
break;
case Token::kMUL:
__ MulImmediate(out, left, right, compiler::kFourBytes);
break;
default:
UNREACHABLE();
}
} else {
Register right = locs()->in(1).reg();
switch (op_kind()) {
case Token::kBIT_AND:
__ and_(out, left, right);
break;
case Token::kBIT_OR:
__ or_(out, left, right);
break;
case Token::kBIT_XOR:
__ xor_(out, left, right);
break;
case Token::kADD:
#if XLEN == 32
__ add(out, left, right);
__ add(out, left, right);
#elif XLEN > 32
__ addw(out, left, right);
__ addw(out, left, right);
#endif
break;
case Token::kSUB:
break;
case Token::kSUB:
#if XLEN == 32
__ sub(out, left, right);
__ sub(out, left, right);
#elif XLEN > 32
__ subw(out, left, right);
__ subw(out, left, right);
#endif
break;
case Token::kMUL:
__ mul(out, left, right);
break;
default:
UNREACHABLE();
break;
case Token::kMUL:
__ mul(out, left, right);
break;
default:
UNREACHABLE();
}
}
}
+54 -43
View File
@@ -6344,58 +6344,69 @@ LocationSummary* BinaryUint32OpInstr::MakeLocationSummary(Zone* zone,
LocationSummary* summary = new (zone)
LocationSummary(zone, kNumInputs, kNumTemps, LocationSummary::kNoCall);
summary->set_in(0, Location::RequiresRegister());
summary->set_in(1, Location::RequiresRegister());
summary->set_in(1, LocationRegisterOrConstant(right()));
summary->set_out(0, Token::IsCommutativeOp(op_kind())
? Location::SameAsFirstOrSecondInput()
: Location::SameAsFirstInput());
return summary;
}
template <typename OperandType>
static void EmitIntegerArithmetic(FlowGraphCompiler* compiler,
Token::Kind op_kind,
Register left,
const OperandType& right) {
switch (op_kind) {
case Token::kADD:
__ addl(left, right);
break;
case Token::kSUB:
__ subl(left, right);
break;
case Token::kBIT_AND:
__ andl(left, right);
break;
case Token::kBIT_OR:
__ orl(left, right);
break;
case Token::kBIT_XOR:
__ xorl(left, right);
break;
case Token::kMUL:
__ imull(left, right);
break;
default:
UNREACHABLE();
}
}
void BinaryUint32OpInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
Register left = locs()->in(0).reg();
Register right = locs()->in(1).reg();
Register out = locs()->out(0).reg();
Register left = locs()->in(0).reg();
ASSERT(out == left);
switch (op_kind()) {
case Token::kBIT_AND:
case Token::kBIT_OR:
case Token::kBIT_XOR:
case Token::kADD:
case Token::kSUB:
case Token::kMUL:
EmitIntegerArithmetic(compiler, op_kind(), left, right);
return;
default:
UNREACHABLE();
if (locs()->in(1).IsConstant()) {
int64_t value;
const bool ok = compiler::HasIntegerValue(locs()->in(1).constant(), &value);
RELEASE_ASSERT(ok);
compiler::Immediate right =
compiler::Immediate(static_cast<int32_t>(static_cast<uint32_t>(value)));
switch (op_kind()) {
case Token::kADD:
__ addl(left, right);
break;
case Token::kSUB:
__ subl(left, right);
break;
case Token::kBIT_AND:
__ andl(left, right);
break;
case Token::kBIT_OR:
__ orl(left, right);
break;
case Token::kBIT_XOR:
__ xorl(left, right);
break;
case Token::kMUL:
__ imull(left, right);
break;
default:
UNREACHABLE();
}
} else {
Register right = locs()->in(1).reg();
switch (op_kind()) {
case Token::kADD:
__ addl(left, right);
break;
case Token::kSUB:
__ subl(left, right);
break;
case Token::kBIT_AND:
__ andl(left, right);
break;
case Token::kBIT_OR:
__ orl(left, right);
break;
case Token::kBIT_XOR:
__ xorl(left, right);
break;
case Token::kMUL:
__ imull(left, right);
break;
default:
UNREACHABLE();
}
}
}