[vm/test] Add IL matching of abstract instructions.

Also marks CheckBoundBase as an abstract instruction, instead of it
being treated specially.

Now when matching IL graphs that use bounds check instructions, which
differ in JIT vs. AOT, the matcher can match the abstract base
instruction (CheckBoundBase) instead of having to separately match the
concrete subclasses on JIT (CheckArrayBound) and AOT
(GenericCheckBound).

TEST=ci

Change-Id: I73dd6bf6711a99cc2c52a69cf31f22ba4850f2b3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/327001
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
This commit is contained in:
Tess Strickland 2023-09-20 14:37:05 +00:00 committed by Commit Queue
parent f5208f5d59
commit c562719814
7 changed files with 29 additions and 25 deletions

View file

@ -3180,8 +3180,8 @@ void NullErrorSlowPath::EmitSharedStubCall(FlowGraphCompiler* compiler,
void RangeErrorSlowPath::PushArgumentsForRuntimeCall(
FlowGraphCompiler* compiler) {
LocationSummary* locs = instruction()->locs();
__ PushRegisterPair(locs->in(CheckBoundBase::kIndexPos).reg(),
locs->in(CheckBoundBase::kLengthPos).reg());
__ PushRegisterPair(locs->in(CheckBoundBaseInstr::kIndexPos).reg(),
locs->in(CheckBoundBaseInstr::kLengthPos).reg());
}
void RangeErrorSlowPath::EmitSharedStubCall(FlowGraphCompiler* compiler,

View file

@ -548,7 +548,7 @@ Value* AssertBooleanInstr::RedefinedValue() const {
return value();
}
Value* CheckBoundBase::RedefinedValue() const {
Value* CheckBoundBaseInstr::RedefinedValue() const {
return index();
}
@ -6483,7 +6483,7 @@ bool CheckArrayBoundInstr::IsFixedLengthArrayType(intptr_t cid) {
return LoadFieldInstr::IsFixedLengthArrayCid(cid);
}
Definition* CheckBoundBase::Canonicalize(FlowGraph* flow_graph) {
Definition* CheckBoundBaseInstr::Canonicalize(FlowGraph* flow_graph) {
return IsRedundant() ? index()->definition() : this;
}

View file

@ -47,7 +47,7 @@ class BlockEntryWithInitialDefs;
class BoxIntegerInstr;
class CallTargets;
class CatchBlockEntryInstr;
class CheckBoundBase;
class CheckBoundBaseInstr;
class ComparisonInstr;
class Definition;
class Environment;
@ -553,6 +553,7 @@ struct InstrAttrs {
M(BinaryIntegerOp, _) \
M(BlockEntry, _) \
M(BoxInteger, _) \
M(CheckBoundBase, _) \
M(Comparison, _) \
M(InstanceCallBase, _) \
M(ShiftIntegerOp, _) \
@ -1143,7 +1144,6 @@ class Instruction : public ZoneAllocated {
DECLARE_INSTRUCTION_TYPE_CHECK(Definition, Definition)
DECLARE_INSTRUCTION_TYPE_CHECK(BlockEntryWithInitialDefs,
BlockEntryWithInitialDefs)
DECLARE_INSTRUCTION_TYPE_CHECK(CheckBoundBase, CheckBoundBase)
FOR_EACH_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
@ -10112,9 +10112,9 @@ class CheckClassIdInstr : public TemplateInstruction<1, NoThrow> {
// Base class for speculative [CheckArrayBoundInstr] and
// non-speculative [GenericCheckBoundInstr] bounds checking.
class CheckBoundBase : public TemplateDefinition<2, NoThrow, Pure> {
class CheckBoundBaseInstr : public TemplateDefinition<2, NoThrow, Pure> {
public:
CheckBoundBase(Value* length, Value* index, intptr_t deopt_id)
CheckBoundBaseInstr(Value* length, Value* index, intptr_t deopt_id)
: TemplateDefinition(deopt_id) {
SetInputAt(kLengthPos, length);
SetInputAt(kIndexPos, index);
@ -10125,8 +10125,8 @@ class CheckBoundBase : public TemplateDefinition<2, NoThrow, Pure> {
virtual Definition* Canonicalize(FlowGraph* flow_graph);
virtual CheckBoundBase* AsCheckBoundBase() { return this; }
virtual const CheckBoundBase* AsCheckBoundBase() const { return this; }
DECLARE_ABSTRACT_INSTRUCTION(CheckBoundBase);
virtual Value* RedefinedValue() const;
// Returns true if the bounds check can be eliminated without
@ -10136,10 +10136,10 @@ class CheckBoundBase : public TemplateDefinition<2, NoThrow, Pure> {
// Give a name to the location/input indices.
enum { kLengthPos = 0, kIndexPos = 1 };
DECLARE_EMPTY_SERIALIZATION(CheckBoundBase, TemplateDefinition)
DECLARE_EMPTY_SERIALIZATION(CheckBoundBaseInstr, TemplateDefinition)
private:
DISALLOW_COPY_AND_ASSIGN(CheckBoundBase);
DISALLOW_COPY_AND_ASSIGN(CheckBoundBaseInstr);
};
// Performs an array bounds check, where
@ -10147,10 +10147,10 @@ class CheckBoundBase : public TemplateDefinition<2, NoThrow, Pure> {
// returns the "safe" index when
// 0 <= index < length
// or otherwise deoptimizes (viz. speculative).
class CheckArrayBoundInstr : public CheckBoundBase {
class CheckArrayBoundInstr : public CheckBoundBaseInstr {
public:
CheckArrayBoundInstr(Value* length, Value* index, intptr_t deopt_id)
: CheckBoundBase(length, index, deopt_id), generalized_(false) {}
: CheckBoundBaseInstr(length, index, deopt_id), generalized_(false) {}
DECLARE_INSTRUCTION(CheckArrayBound)
@ -10171,7 +10171,7 @@ class CheckArrayBoundInstr : public CheckBoundBase {
#define FIELD_LIST(F) F(bool, generalized_)
DECLARE_INSTRUCTION_SERIALIZABLE_FIELDS(CheckArrayBoundInstr,
CheckBoundBase,
CheckBoundBaseInstr,
FIELD_LIST)
#undef FIELD_LIST
@ -10184,7 +10184,7 @@ class CheckArrayBoundInstr : public CheckBoundBase {
// returns the "safe" index when
// 0 <= index < length
// or otherwise throws an out-of-bounds exception (viz. non-speculative).
class GenericCheckBoundInstr : public CheckBoundBase {
class GenericCheckBoundInstr : public CheckBoundBaseInstr {
public:
// We prefer to have unboxed inputs on 64-bit where values can fit into a
// register.
@ -10193,7 +10193,7 @@ class GenericCheckBoundInstr : public CheckBoundBase {
}
GenericCheckBoundInstr(Value* length, Value* index, intptr_t deopt_id)
: CheckBoundBase(length, index, deopt_id) {}
: CheckBoundBaseInstr(length, index, deopt_id) {}
virtual bool AttributesEqual(const Instruction& other) const { return true; }
@ -10230,7 +10230,7 @@ class GenericCheckBoundInstr : public CheckBoundBase {
return SlowPathSharingSupported(is_optimizing);
}
DECLARE_EMPTY_SERIALIZATION(GenericCheckBoundInstr, CheckBoundBase)
DECLARE_EMPTY_SERIALIZATION(GenericCheckBoundInstr, CheckBoundBaseInstr)
private:
DISALLOW_COPY_AND_ASSIGN(GenericCheckBoundInstr);

View file

@ -410,6 +410,7 @@ Instruction* ILMatcher::MatchInternal(std::vector<MatchCode> match_codes,
return cursor; \
}
FOR_EACH_INSTRUCTION(EMIT_CASE)
FOR_EACH_ABSTRACT_INSTRUCTION(EMIT_CASE)
#undef EMIT_CASE
default:
UNREACHABLE();
@ -451,6 +452,7 @@ const char* ILMatcher::MatchOpCodeToCString(MatchOpCode opcode) {
case kMatchAndMoveOptional##Instruction: \
return "kMatchAndMoveOptional" #Instruction;
FOR_EACH_INSTRUCTION(EMIT_CASE)
FOR_EACH_ABSTRACT_INSTRUCTION(EMIT_CASE)
#undef EMIT_CASE
default:
UNREACHABLE();

View file

@ -118,6 +118,7 @@ enum MatchOpCode {
kMatch##Instruction, kMatchAndMove##Instruction, \
kMatchAndMoveOptional##Instruction,
FOR_EACH_INSTRUCTION(DEFINE_MATCH_OPCODES)
FOR_EACH_ABSTRACT_INSTRUCTION(DEFINE_MATCH_OPCODES)
#undef DEFINE_MATCH_OPCODES
// Matches a branch and moves left.
@ -161,6 +162,7 @@ class MatchCode {
RELEASE_ASSERT(opcode == kMatch##Type || opcode == kMatchAndMove##Type); \
}
FOR_EACH_INSTRUCTION(DEFINE_TYPED_CONSTRUCTOR)
FOR_EACH_ABSTRACT_INSTRUCTION(DEFINE_TYPED_CONSTRUCTOR)
#undef DEFINE_TYPED_CONSTRUCTOR
MatchOpCode opcode() { return opcode_; }

View file

@ -222,17 +222,17 @@ void RangeAnalysis::InsertConstraintsFor(Definition* defn) {
}
void RangeAnalysis::ConstrainValueAfterCheckBound(Value* use,
CheckBoundBase* check,
CheckBoundBaseInstr* check,
Definition* defn) {
const intptr_t use_index = use->use_index();
Range* constraint_range = nullptr;
if (use_index == CheckBoundBase::kIndexPos) {
if (use_index == CheckBoundBaseInstr::kIndexPos) {
Definition* length = check->length()->definition();
constraint_range = new (Z) Range(RangeBoundary::FromConstant(0),
RangeBoundary::FromDefinition(length, -1));
} else {
ASSERT(use_index == CheckBoundBase::kLengthPos);
ASSERT(use_index == CheckBoundBaseInstr::kLengthPos);
Definition* index = check->index()->definition();
constraint_range = new (Z)
Range(RangeBoundary::FromDefinition(index, 1), RangeBoundary::MaxSmi());
@ -1349,7 +1349,7 @@ void RangeAnalysis::EliminateRedundantBoundsChecks() {
!CompilerState::Current().is_aot() &&
!function.ProhibitsBoundsCheckGeneralization();
BoundsCheckGeneralizer generalizer(this, flow_graph_);
for (CheckBoundBase* check : bounds_checks_) {
for (CheckBoundBaseInstr* check : bounds_checks_) {
if (check->IsRedundant(/*use_loops=*/true)) {
check->ReplaceUsesWith(check->index()->definition());
check->RemoveFromGraph();
@ -3181,7 +3181,7 @@ static bool IsRedundantBasedOnRangeInformation(Value* index, Value* length) {
return false;
}
bool CheckBoundBase::IsRedundant(bool use_loops) {
bool CheckBoundBaseInstr::IsRedundant(bool use_loops) {
// First, try to prove redundancy with the results of range analysis.
if (IsRedundantBasedOnRangeInformation(index(), length())) {
return true;

View file

@ -632,7 +632,7 @@ class RangeAnalysis : public ValueObject {
bool ConstrainValueAfterBranch(Value* use, Definition* defn);
void ConstrainValueAfterCheckBound(Value* use,
CheckBoundBase* check,
CheckBoundBaseInstr* check,
Definition* defn);
// Infer ranges for integer (smi or mint) definitions.
@ -679,7 +679,7 @@ class RangeAnalysis : public ValueObject {
GrowableArray<ShiftIntegerOpInstr*> shift_int64_ops_;
// All CheckArrayBound/GenericCheckBound instructions.
GrowableArray<CheckBoundBase*> bounds_checks_;
GrowableArray<CheckBoundBaseInstr*> bounds_checks_;
// All Constraints inserted during InsertConstraints phase. They are treated
// as smi values.