Commit 1b230799 authored by ahaas's avatar ahaas Committed by Commit bot

[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}
parent 2cd9877b
......@@ -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),
......
......@@ -46,6 +46,7 @@ namespace compiler {
V(ArmUxtab) \
V(ArmRbit) \
V(ArmUxtah) \
V(ArmAddPair) \
V(ArmPairLsl) \
V(ArmVcmpF32) \
V(ArmVaddF32) \
......
......@@ -48,6 +48,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArmUxtab:
case kArmUxtah:
case kArmRbit:
case kArmAddPair:
case kArmPairLsl:
case kArmVcmpF32:
case kArmVaddF32:
......
......@@ -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));
......
......@@ -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));
......
......@@ -29,6 +29,7 @@ namespace compiler {
V(IA32Shl) \
V(IA32Shr) \
V(IA32Sar) \
V(IA32AddPair) \
V(IA32ShlPair) \
V(IA32ShrPair) \
V(IA32SarPair) \
......
......@@ -31,6 +31,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kIA32Shl:
case kIA32Shr:
case kIA32Sar:
case kIA32AddPair:
case kIA32ShlPair:
case kIA32ShrPair:
case kIA32SarPair:
......
......@@ -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);
......
......@@ -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:
......
......@@ -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:
......
......@@ -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)
......
......@@ -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();
......
......@@ -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(); }
......
......@@ -334,6 +334,7 @@
V(LoadParentFramePointer) \
V(CheckedLoad) \
V(CheckedStore) \
V(Int32PairAdd) \
V(Word32PairShl) \
V(Word32PairShr) \
V(Word32PairSar)
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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(); }
......
......@@ -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:
......
......@@ -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;
......
......@@ -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);
......
......@@ -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},
......
......@@ -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;
......
......@@ -96,6 +96,7 @@ TEST(DisasmIa320) {
__ nop();
__ add(ebx, Immediate(12));
__ nop();
__ adc(edx, Operand(ebx));
__ adc(ecx, 12);
__ adc(ecx, 1000);
__ nop();
......
......@@ -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:
......
......@@ -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:
......
......@@ -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, \
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment