[wasm] Int64Lowering of Int64Add on ia32 and arm.

Int64Add is lowered to a new turbofan operator, Int32AddPair. The new
operator takes 4 inputs an generates 2 outputs. The inputs are the low
word of the left input, high word of the left input, the low word of the
right input, and high word of the right input. The ouputs are the low
and high word of the result of the addition.

R=titzer@chromium.org, v8-arm-ports@googlegroups.com

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

Cr-Commit-Position: refs/heads/master@{#34747}
This commit is contained in:
ahaas 2016-03-14 08:33:15 -07:00 committed by Commit bot
parent 2cd9877b6d
commit 1b23079936
27 changed files with 282 additions and 27 deletions

View File

@ -787,6 +787,17 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ teq(i.InputRegister(0), i.InputOperand2(1));
DCHECK_EQ(SetCC, i.OutputSBit());
break;
case kArmAddPair:
// i.InputRegister(0) ... left low word.
// i.InputRegister(1) ... left high word.
// i.InputRegister(2) ... right low word.
// i.InputRegister(3) ... right high word.
__ add(i.OutputRegister(0), i.InputRegister(0), i.InputRegister(2),
SBit::SetCC);
__ adc(i.OutputRegister(1), i.InputRegister(1),
Operand(i.InputRegister(3)));
DCHECK_EQ(LeaveCC, i.OutputSBit());
break;
case kArmPairLsl:
if (instr->InputAt(2)->IsImmediate()) {
__ PairLsl(i.OutputRegister(0), i.OutputRegister(1), i.InputRegister(0),

View File

@ -46,6 +46,7 @@ namespace compiler {
V(ArmUxtab) \
V(ArmRbit) \
V(ArmUxtah) \
V(ArmAddPair) \
V(ArmPairLsl) \
V(ArmVcmpF32) \
V(ArmVaddF32) \

View File

@ -48,6 +48,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArmUxtab:
case kArmUxtah:
case kArmRbit:
case kArmAddPair:
case kArmPairLsl:
case kArmVcmpF32:
case kArmVaddF32:

View File

@ -767,6 +767,22 @@ void InstructionSelector::VisitWord32Sar(Node* node) {
VisitShift(this, node, TryMatchASR);
}
void InstructionSelector::VisitInt32PairAdd(Node* node) {
ArmOperandGenerator g(this);
// We use UseUniqueRegister here to avoid register sharing with the output
// registers.
InstructionOperand inputs[] = {
g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
InstructionOperand outputs[] = {
g.DefineAsRegister(node),
g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
Emit(kArmAddPair, 2, outputs, 4, inputs);
}
void InstructionSelector::VisitWord32PairShl(Node* node) {
ArmOperandGenerator g(this);
Int32Matcher m(node->InputAt(2));

View File

@ -677,6 +677,31 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
__ sar_cl(i.OutputOperand());
}
break;
case kIA32AddPair: {
// i.OutputRegister(0) == i.InputRegister(0) ... left low word.
// i.InputRegister(1) ... left high word.
// i.InputRegister(2) ... right low word.
// i.InputRegister(3) ... right high word.
bool use_temp = false;
if (i.OutputRegister(0).code() == i.InputRegister(1).code() ||
i.OutputRegister(0).code() == i.InputRegister(3).code()) {
// We cannot write to the output register directly, because it would
// overwrite an input for adc. We have to use the temp register.
use_temp = true;
__ Move(i.TempRegister(0), i.InputRegister(0));
__ add(i.TempRegister(0), i.InputRegister(2));
} else {
__ add(i.OutputRegister(0), i.InputRegister(2));
}
__ adc(i.InputRegister(1), Operand(i.InputRegister(3)));
if (i.OutputRegister(1).code() != i.InputRegister(1).code()) {
__ Move(i.OutputRegister(1), i.InputRegister(1));
}
if (use_temp) {
__ Move(i.OutputRegister(0), i.TempRegister(0));
}
break;
}
case kIA32ShlPair:
if (HasImmediateInput(instr, 2)) {
__ ShlPair(i.InputRegister(1), i.InputRegister(0), i.InputInt6(2));

View File

@ -29,6 +29,7 @@ namespace compiler {
V(IA32Shl) \
V(IA32Shr) \
V(IA32Sar) \
V(IA32AddPair) \
V(IA32ShlPair) \
V(IA32ShrPair) \
V(IA32SarPair) \

View File

@ -31,6 +31,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kIA32Shl:
case kIA32Shr:
case kIA32Sar:
case kIA32AddPair:
case kIA32ShlPair:
case kIA32ShrPair:
case kIA32SarPair:

View File

@ -583,6 +583,24 @@ void InstructionSelector::VisitWord32Sar(Node* node) {
VisitShift(this, node, kIA32Sar);
}
void InstructionSelector::VisitInt32PairAdd(Node* node) {
IA32OperandGenerator g(this);
// We use UseUniqueRegister here to avoid register sharing with the temp
// register.
InstructionOperand inputs[] = {
g.UseRegister(node->InputAt(0)), g.UseUniqueRegister(node->InputAt(1)),
g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
InstructionOperand outputs[] = {
g.DefineSameAsFirst(node),
g.DefineAsRegister(NodeProperties::FindProjection(node, 1))};
InstructionOperand temps[] = {g.TempRegister()};
Emit(kIA32AddPair, 2, outputs, 4, inputs, 1, temps);
}
void InstructionSelector::VisitWord32PairShl(Node* node) {
IA32OperandGenerator g(this);

View File

@ -1150,6 +1150,10 @@ void InstructionSelector::VisitNode(Node* node) {
}
case IrOpcode::kCheckedStore:
return VisitCheckedStore(node);
case IrOpcode::kInt32PairAdd:
MarkAsWord32(NodeProperties::FindProjection(node, 0));
MarkAsWord32(NodeProperties::FindProjection(node, 1));
return VisitInt32PairAdd(node);
case IrOpcode::kWord32PairShl:
MarkAsWord32(NodeProperties::FindProjection(node, 0));
MarkAsWord32(NodeProperties::FindProjection(node, 1));
@ -1387,6 +1391,8 @@ void InstructionSelector::VisitBitcastInt64ToFloat64(Node* node) {
// 32 bit targets do not implement the following instructions.
#if V8_TARGET_ARCH_64_BIT
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShr(Node* node) { UNIMPLEMENTED(); }
@ -1471,6 +1477,7 @@ void InstructionSelector::VisitProjection(Node* node) {
case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTryTruncateFloat32ToUint64:
case IrOpcode::kTryTruncateFloat64ToUint64:
case IrOpcode::kInt32PairAdd:
case IrOpcode::kWord32PairShl:
case IrOpcode::kWord32PairShr:
case IrOpcode::kWord32PairSar:

View File

@ -251,6 +251,25 @@ void Int64Lowering::LowerNode(Node* node) {
// todo(ahaas): I added a list of missing instructions here to make merging
// easier when I do them one by one.
// kExprI64Add:
case IrOpcode::kInt64Add: {
DCHECK(node->InputCount() == 2);
Node* right = node->InputAt(1);
node->ReplaceInput(1, GetReplacementLow(right));
node->AppendInput(zone(), GetReplacementHigh(right));
Node* left = node->InputAt(0);
node->ReplaceInput(0, GetReplacementLow(left));
node->InsertInput(zone(), 1, GetReplacementHigh(left));
NodeProperties::ChangeOp(node, machine()->Int32PairAdd());
// We access the additional return values through projections.
Node* low_node = graph()->NewNode(common()->Projection(0), node);
Node* high_node = graph()->NewNode(common()->Projection(1), node);
ReplaceNode(node, low_node, high_node);
break;
}
// kExprI64Sub:
// kExprI64Mul:
// kExprI64DivS:

View File

@ -196,6 +196,7 @@ MachineRepresentation StackSlotRepresentationOf(Operator const* op) {
V(LoadStackPointer, Operator::kNoProperties, 0, 0, 1) \
V(LoadFramePointer, Operator::kNoProperties, 0, 0, 1) \
V(LoadParentFramePointer, Operator::kNoProperties, 0, 0, 1) \
V(Int32PairAdd, Operator::kNoProperties, 4, 0, 2) \
V(Word32PairShl, Operator::kNoProperties, 3, 0, 2) \
V(Word32PairShr, Operator::kNoProperties, 3, 0, 2) \
V(Word32PairSar, Operator::kNoProperties, 3, 0, 2)

View File

@ -179,6 +179,7 @@ class MachineOperatorBuilder final : public ZoneObject {
const OptionalOperator Word64Ctz();
const Operator* Word64Equal();
const Operator* Int32PairAdd();
const Operator* Word32PairShl();
const Operator* Word32PairShr();
const Operator* Word32PairSar();

View File

@ -395,6 +395,8 @@ void InstructionSelector::VisitWord32Sar(Node* node) {
VisitRRO(this, kMipsSar, node);
}
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShr(Node* node) { UNIMPLEMENTED(); }

View File

@ -334,6 +334,7 @@
V(LoadParentFramePointer) \
V(CheckedLoad) \
V(CheckedStore) \
V(Int32PairAdd) \
V(Word32PairShl) \
V(Word32PairShr) \
V(Word32PairSar)

View File

@ -898,6 +898,9 @@ void InstructionSelector::VisitInt64Add(Node* node) {
}
#endif
#if !V8_TARGET_ARCH_PPC64
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
#endif
void InstructionSelector::VisitInt32Sub(Node* node) {
PPCOperandGenerator g(this);

View File

@ -324,6 +324,9 @@ class RawMachineAssembler {
Node* Uint64Mod(Node* a, Node* b) {
return AddNode(machine()->Uint64Mod(), a, b);
}
Node* Int32PairAdd(Node* a_low, Node* a_high, Node* b_low, Node* b_high) {
return AddNode(machine()->Int32PairAdd(), a_low, a_high, b_low, b_high);
}
Node* Word32PairShl(Node* low_word, Node* high_word, Node* shift) {
return AddNode(machine()->Word32PairShl(), low_word, high_word, shift);
}

View File

@ -2441,6 +2441,8 @@ Type* Typer::Visitor::TypeCheckedStore(Node* node) {
return nullptr;
}
Type* Typer::Visitor::TypeInt32PairAdd(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeWord32PairShl(Node* node) { return Type::Internal(); }
Type* Typer::Visitor::TypeWord32PairShr(Node* node) { return Type::Internal(); }

View File

@ -946,6 +946,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kFloat64ExtractHighWord32:
case IrOpcode::kFloat64InsertLowWord32:
case IrOpcode::kFloat64InsertHighWord32:
case IrOpcode::kInt32PairAdd:
case IrOpcode::kWord32PairShl:
case IrOpcode::kWord32PairShr:
case IrOpcode::kWord32PairSar:

View File

@ -494,6 +494,9 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
// todo(ahaas): I added a list of missing instructions here to make merging
// easier when I do them one by one.
// kExprI64Add:
case wasm::kExprI64Add:
op = m->Int64Add();
break;
// kExprI64Sub:
// kExprI64Mul:
// kExprI64DivS:
@ -559,9 +562,6 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left,
#if WASM_64
// Opcodes only supported on 64-bit platforms.
// TODO(titzer): query the machine operator builder here instead of #ifdef.
case wasm::kExprI64Add:
op = m->Int64Add();
break;
case wasm::kExprI64Sub:
op = m->Int64Sub();
break;

View File

@ -544,6 +544,8 @@ void InstructionSelector::VisitWord32Sar(Node* node) {
VisitShift(this, node, kX87Sar);
}
void InstructionSelector::VisitInt32PairAdd(Node* node) { UNIMPLEMENTED(); }
void InstructionSelector::VisitWord32PairShl(Node* node) {
X87OperandGenerator g(this);

View File

@ -29,29 +29,18 @@ struct ByteMnemonic {
};
static const ByteMnemonic two_operands_instr[] = {
{0x01, "add", OPER_REG_OP_ORDER},
{0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER},
{0x0B, "or", REG_OPER_OP_ORDER},
{0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER},
{0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER},
{0x2A, "subb", REG_OPER_OP_ORDER},
{0x2B, "sub", REG_OPER_OP_ORDER},
{0x31, "xor", OPER_REG_OP_ORDER},
{0x33, "xor", REG_OPER_OP_ORDER},
{0x38, "cmpb", OPER_REG_OP_ORDER},
{0x39, "cmp", OPER_REG_OP_ORDER},
{0x3A, "cmpb", REG_OPER_OP_ORDER},
{0x3B, "cmp", REG_OPER_OP_ORDER},
{0x84, "test_b", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER},
{0x87, "xchg", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER},
{0x8B, "mov", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER},
{-1, "", UNSET_OP_ORDER}};
{0x01, "add", OPER_REG_OP_ORDER}, {0x03, "add", REG_OPER_OP_ORDER},
{0x09, "or", OPER_REG_OP_ORDER}, {0x0B, "or", REG_OPER_OP_ORDER},
{0x13, "adc", REG_OPER_OP_ORDER}, {0x1B, "sbb", REG_OPER_OP_ORDER},
{0x21, "and", OPER_REG_OP_ORDER}, {0x23, "and", REG_OPER_OP_ORDER},
{0x29, "sub", OPER_REG_OP_ORDER}, {0x2A, "subb", REG_OPER_OP_ORDER},
{0x2B, "sub", REG_OPER_OP_ORDER}, {0x31, "xor", OPER_REG_OP_ORDER},
{0x33, "xor", REG_OPER_OP_ORDER}, {0x38, "cmpb", OPER_REG_OP_ORDER},
{0x39, "cmp", OPER_REG_OP_ORDER}, {0x3A, "cmpb", REG_OPER_OP_ORDER},
{0x3B, "cmp", REG_OPER_OP_ORDER}, {0x84, "test_b", REG_OPER_OP_ORDER},
{0x85, "test", REG_OPER_OP_ORDER}, {0x87, "xchg", REG_OPER_OP_ORDER},
{0x8A, "mov_b", REG_OPER_OP_ORDER}, {0x8B, "mov", REG_OPER_OP_ORDER},
{0x8D, "lea", REG_OPER_OP_ORDER}, {-1, "", UNSET_OP_ORDER}};
static const ByteMnemonic zero_operands_instr[] = {
{0xC3, "ret", UNSET_OP_ORDER},

View File

@ -4197,6 +4197,74 @@ TEST(RunTruncateFloat64ToFloat32) {
FOR_FLOAT64_INPUTS(i) { CHECK_FLOAT_EQ(DoubleToFloat32(*i), m.Call(*i)); }
}
int64_t ToInt64(uint32_t low, uint32_t high) {
return (static_cast<int64_t>(high) << 32) | static_cast<int64_t>(low);
}
#if V8_TARGET_ARCH_32_BIT
TEST(RunInt32PairAdd) {
BufferedRawMachineAssemblerTester<int32_t> m(
MachineType::Int32(), MachineType::Int32(), MachineType::Int32(),
MachineType::Int32());
uint32_t high;
uint32_t low;
Node* PairAdd = m.Int32PairAdd(m.Parameter(0), m.Parameter(1), m.Parameter(2),
m.Parameter(3));
m.StoreToPointer(&low, MachineRepresentation::kWord32,
m.Projection(0, PairAdd));
m.StoreToPointer(&high, MachineRepresentation::kWord32,
m.Projection(1, PairAdd));
m.Return(m.Int32Constant(74));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) {
m.Call(static_cast<int32_t>(*i & 0xffffffff),
static_cast<int32_t>(*i >> 32),
static_cast<int32_t>(*j & 0xffffffff),
static_cast<int32_t>(*j >> 32));
CHECK_EQ(*i + *j, ToInt64(low, high));
}
}
}
void TestInt32PairAddWithSharedInput(int a, int b, int c, int d) {
BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Uint32(),
MachineType::Uint32());
uint32_t high;
uint32_t low;
Node* PairAdd = m.Int32PairAdd(m.Parameter(a), m.Parameter(b), m.Parameter(c),
m.Parameter(d));
m.StoreToPointer(&low, MachineRepresentation::kWord32,
m.Projection(0, PairAdd));
m.StoreToPointer(&high, MachineRepresentation::kWord32,
m.Projection(1, PairAdd));
m.Return(m.Int32Constant(74));
FOR_UINT32_INPUTS(i) {
FOR_UINT32_INPUTS(j) {
m.Call(*i, *j);
uint32_t inputs[] = {*i, *j};
CHECK_EQ(ToInt64(inputs[a], inputs[b]) + ToInt64(inputs[c], inputs[d]),
ToInt64(low, high));
}
}
}
TEST(RunInt32PairAddWithSharedInput) {
TestInt32PairAddWithSharedInput(0, 0, 0, 0);
TestInt32PairAddWithSharedInput(1, 0, 0, 0);
TestInt32PairAddWithSharedInput(0, 1, 0, 0);
TestInt32PairAddWithSharedInput(0, 0, 1, 0);
TestInt32PairAddWithSharedInput(0, 0, 0, 1);
TestInt32PairAddWithSharedInput(1, 1, 0, 0);
}
#endif
TEST(RunDeadChangeFloat64ToInt32) {
RawMachineAssemblerTester<int32_t> m;

View File

@ -96,6 +96,7 @@ TEST(DisasmIa320) {
__ nop();
__ add(ebx, Immediate(12));
__ nop();
__ adc(edx, Operand(ebx));
__ adc(ecx, 12);
__ adc(ecx, 1000);
__ nop();

View File

@ -35,7 +35,7 @@
V(I64Return, true) \
V(I64Param, true) \
V(I64LoadStore, true) \
V(I64Add, false) \
V(I64Add, true) \
V(I64Sub, false) \
V(I64Mul, false) \
V(I64DivS, false) \
@ -118,6 +118,14 @@ TEST(Run_Wasm_Return_I64) {
// todo(ahaas): I added a list of missing instructions here to make merging
// easier when I do them one by one.
// kExprI64Add:
TEST(Run_WasmI64Add) {
REQUIRE(I64Add);
WasmRunner<int64_t> r(MachineType::Int64(), MachineType::Int64());
BUILD(r, WASM_I64_ADD(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
FOR_INT64_INPUTS(i) {
FOR_INT64_INPUTS(j) { CHECK_EQ(*i + *j, r.Call(*i, *j)); }
}
}
// kExprI64Sub:
// kExprI64Mul:
// kExprI64DivS:

View File

@ -302,6 +302,21 @@ TEST_F(Int64LoweringTest, CallI64Parameter) {
// todo(ahaas): I added a list of missing instructions here to make merging
// easier when I do them one by one.
// kExprI64Add:
TEST_F(Int64LoweringTest, Int64Add) {
LowerGraph(graph()->NewNode(machine()->Int64Add(), Int64Constant(value(0)),
Int64Constant(value(1))),
MachineRepresentation::kWord64);
Capture<Node*> add;
Matcher<Node*> add_matcher = IsInt32PairAdd(
IsInt32Constant(low_word_value(0)), IsInt32Constant(high_word_value(0)),
IsInt32Constant(low_word_value(1)), IsInt32Constant(high_word_value(1)));
EXPECT_THAT(graph()->end()->InputAt(1),
IsReturn2(IsProjection(0, AllOf(CaptureEq(&add), add_matcher)),
IsProjection(1, AllOf(CaptureEq(&add), add_matcher)),
start(), start()));
}
// kExprI64Sub:
// kExprI64Mul:
// kExprI64DivS:

View File

@ -1406,6 +1406,50 @@ class IsLoadContextMatcher final : public NodeMatcher {
const Matcher<Node*> context_matcher_;
};
class IsQuadopMatcher final : public NodeMatcher {
public:
IsQuadopMatcher(IrOpcode::Value opcode, const Matcher<Node*>& a_matcher,
const Matcher<Node*>& b_matcher,
const Matcher<Node*>& c_matcher,
const Matcher<Node*>& d_matcher)
: NodeMatcher(opcode),
a_matcher_(a_matcher),
b_matcher_(b_matcher),
c_matcher_(c_matcher),
d_matcher_(d_matcher) {}
void DescribeTo(std::ostream* os) const final {
NodeMatcher::DescribeTo(os);
*os << " whose a (";
a_matcher_.DescribeTo(os);
*os << ") and b (";
b_matcher_.DescribeTo(os);
*os << ") and c (";
c_matcher_.DescribeTo(os);
*os << ") and d (";
d_matcher_.DescribeTo(os);
*os << ")";
}
bool MatchAndExplain(Node* node, MatchResultListener* listener) const final {
return (NodeMatcher::MatchAndExplain(node, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 0), "a",
a_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 1), "b",
b_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 2), "c",
c_matcher_, listener) &&
PrintMatchAndExplain(NodeProperties::GetValueInput(node, 3), "d",
d_matcher_, listener));
}
private:
const Matcher<Node*> a_matcher_;
const Matcher<Node*> b_matcher_;
const Matcher<Node*> c_matcher_;
const Matcher<Node*> d_matcher_;
};
class IsTernopMatcher final : public NodeMatcher {
public:
IsTernopMatcher(IrOpcode::Value opcode, const Matcher<Node*>& lhs_matcher,
@ -2104,6 +2148,16 @@ Matcher<Node*> IsLoadFramePointer() {
return MakeMatcher(new NodeMatcher(IrOpcode::kLoadFramePointer));
}
#define IS_QUADOP_MATCHER(Name) \
Matcher<Node*> Is##Name( \
const Matcher<Node*>& a_matcher, const Matcher<Node*>& b_matcher, \
const Matcher<Node*>& c_matcher, const Matcher<Node*>& d_matcher) { \
return MakeMatcher(new IsQuadopMatcher(IrOpcode::k##Name, a_matcher, \
b_matcher, c_matcher, d_matcher)); \
}
IS_QUADOP_MATCHER(Int32PairAdd)
#define IS_TERNOP_MATCHER(Name) \
Matcher<Node*> Is##Name(const Matcher<Node*>& lhs_matcher, \
const Matcher<Node*>& mid_matcher, \

View File

@ -358,6 +358,10 @@ Matcher<Node*> IsNumberToUint32(const Matcher<Node*>& input_matcher);
Matcher<Node*> IsParameter(const Matcher<int> index_matcher);
Matcher<Node*> IsLoadFramePointer();
Matcher<Node*> IsInt32PairAdd(const Matcher<Node*>& a_matcher,
const Matcher<Node*>& b_matcher,
const Matcher<Node*>& c_matcher,
const Matcher<Node*>& d_matcher);
Matcher<Node*> IsWord32PairShl(const Matcher<Node*>& lhs_matcher,
const Matcher<Node*>& mid_matcher,
const Matcher<Node*>& rhs_matcher);