From 889e5795bb7687e9d73566f5884a908f20f8fc2a Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Wed, 9 Oct 2024 16:47:48 +0000 Subject: [PATCH] [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 Commit-Queue: Ryan Macnak --- runtime/tests/vm/dart/uint32_op_imm_test.dart | 283 ++++++++++++++++++ .../vm/compiler/assembler/assembler_arm.cc | 13 + runtime/vm/compiler/assembler/assembler_arm.h | 11 +- .../vm/compiler/assembler/assembler_arm64.h | 12 +- runtime/vm/compiler/backend/il_arm.cc | 76 +++-- runtime/vm/compiler/backend/il_arm64.cc | 78 +++-- runtime/vm/compiler/backend/il_riscv.cc | 80 +++-- runtime/vm/compiler/backend/il_x64.cc | 97 +++--- 8 files changed, 527 insertions(+), 123 deletions(-) create mode 100644 runtime/tests/vm/dart/uint32_op_imm_test.dart diff --git a/runtime/tests/vm/dart/uint32_op_imm_test.dart b/runtime/tests/vm/dart/uint32_op_imm_test.dart new file mode 100644 index 00000000000..3abc108f078 --- /dev/null +++ b/runtime/tests/vm/dart/uint32_op_imm_test.dart @@ -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); +} diff --git a/runtime/vm/compiler/assembler/assembler_arm.cc b/runtime/vm/compiler/assembler/assembler_arm.cc index bf5f79f01d2..03692e62085 100644 --- a/runtime/vm/compiler/assembler/assembler_arm.cc +++ b/runtime/vm/compiler/assembler/assembler_arm.cc @@ -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)) { diff --git a/runtime/vm/compiler/assembler/assembler_arm.h b/runtime/vm/compiler/assembler/assembler_arm.h index 0288bb987bd..a60c916d87b 100644 --- a/runtime/vm/compiler/assembler/assembler_arm.h +++ b/runtime/vm/compiler/assembler/assembler_arm.h @@ -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, diff --git a/runtime/vm/compiler/assembler/assembler_arm64.h b/runtime/vm/compiler/assembler/assembler_arm64.h index 1d63a0c40a3..63a1b0a9fec 100644 --- a/runtime/vm/compiler/assembler/assembler_arm64.h +++ b/runtime/vm/compiler/assembler/assembler_arm64.h @@ -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); } } } diff --git a/runtime/vm/compiler/backend/il_arm.cc b/runtime/vm/compiler/backend/il_arm.cc index 6c81467bc90..7cb7243d4a1 100644 --- a/runtime/vm/compiler/backend/il_arm.cc +++ b/runtime/vm/compiler/backend/il_arm.cc @@ -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(); + } } } diff --git a/runtime/vm/compiler/backend/il_arm64.cc b/runtime/vm/compiler/backend/il_arm64.cc index f2f417d62c6..6ba81c966df 100644 --- a/runtime/vm/compiler/backend/il_arm64.cc +++ b/runtime/vm/compiler/backend/il_arm64.cc @@ -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(); + } } } diff --git a/runtime/vm/compiler/backend/il_riscv.cc b/runtime/vm/compiler/backend/il_riscv.cc index 41d1e0b59cc..9a7babcfb98 100644 --- a/runtime/vm/compiler/backend/il_riscv.cc +++ b/runtime/vm/compiler/backend/il_riscv.cc @@ -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(); + } } } diff --git a/runtime/vm/compiler/backend/il_x64.cc b/runtime/vm/compiler/backend/il_x64.cc index bf857863c7f..64c498bb859 100644 --- a/runtime/vm/compiler/backend/il_x64.cc +++ b/runtime/vm/compiler/backend/il_x64.cc @@ -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 -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(static_cast(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(); + } } }