BuildLoop();
// Populate the loop with instructions: set/get array with different types.
+ // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+ // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, int_constant_, Primitive::kPrimInt, 0);
+ parameter_, int_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0);
+ parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
BuildLoop();
// Populate the loop with instructions: set/get array with same types.
+ // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to
+ // avoid SsaBuilder's typing of ambiguous array operations from reference type info.
HInstruction* get_array = new (&allocator_) HArrayGet(
- parameter_, int_constant_, Primitive::kPrimFloat, 0);
+ parameter_, int_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction());
HInstruction* set_array = new (&allocator_) HArraySet(
- parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0);
+ parameter_, get_array, float_constant_, Primitive::kPrimByte, 0);
loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction());
EXPECT_EQ(get_array->GetBlock(), loop_body_);
static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
? AllWritesAndReads()
- : SideEffects(TypeFlag(type, kFieldWriteOffset));
+ : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset));
}
static SideEffects ArrayWriteOfType(Primitive::Type type) {
- return SideEffects(TypeFlag(type, kArrayWriteOffset));
+ return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset));
}
static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) {
return is_volatile
? AllWritesAndReads()
- : SideEffects(TypeFlag(type, kFieldReadOffset));
+ : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset));
}
static SideEffects ArrayReadOfType(Primitive::Type type) {
- return SideEffects(TypeFlag(type, kArrayReadOffset));
+ return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset));
}
static SideEffects CanTriggerGC() {
static constexpr uint64_t kAllReads =
((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset;
+ // Work around the fact that HIR aliases I/F and J/D.
+ // TODO: remove this interceptor once HIR types are clean
+ static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) {
+ switch (type) {
+ case Primitive::kPrimInt:
+ case Primitive::kPrimFloat:
+ return TypeFlag(Primitive::kPrimInt, offset) |
+ TypeFlag(Primitive::kPrimFloat, offset);
+ case Primitive::kPrimLong:
+ case Primitive::kPrimDouble:
+ return TypeFlag(Primitive::kPrimLong, offset) |
+ TypeFlag(Primitive::kPrimDouble, offset);
+ default:
+ return TypeFlag(type, offset);
+ }
+ }
+
// Translates type to bit flag.
static uint64_t TypeFlag(Primitive::Type type, int offset) {
CHECK_NE(type, Primitive::kPrimVoid);
uint32_t dex_pc,
SideEffects additional_side_effects = SideEffects::None())
: HTemplateInstruction(
- SideEffectsForArchRuntimeCalls(value->GetType()).Union(additional_side_effects),
- dex_pc) {
+ SideEffects::ArrayWriteOfType(expected_component_type).Union(
+ SideEffectsForArchRuntimeCalls(value->GetType())).Union(
+ additional_side_effects),
+ dex_pc) {
SetPackedField<ExpectedComponentTypeField>(expected_component_type);
SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot);
SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
- // We can now call component type logic to set correct type-based side effects.
- AddSideEffects(SideEffects::ArrayWriteOfType(GetComponentType()));
}
bool NeedsEnvironment() const OVERRIDE {
EXPECT_FALSE(any_write.MayDependOn(volatile_read));
}
-TEST(SideEffectsTest, SameWidthTypesNoAlias) {
+TEST(SideEffectsTest, SameWidthTypes) {
// Type I/F.
- testNoWriteAndReadDependence(
+ testWriteAndReadDependence(
SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false),
SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false));
- testNoWriteAndReadDependence(
+ testWriteAndReadDependence(
SideEffects::ArrayWriteOfType(Primitive::kPrimInt),
SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
// Type L/D.
- testNoWriteAndReadDependence(
+ testWriteAndReadDependence(
SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false),
SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false));
- testNoWriteAndReadDependence(
+ testWriteAndReadDependence(
SideEffects::ArrayWriteOfType(Primitive::kPrimLong),
SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
}
"||||||L|",
SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str());
EXPECT_STREQ(
- "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|",
- SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str());
- EXPECT_STREQ(
"|||||Z||",
SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str());
EXPECT_STREQ(
- "|||||C||",
- SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str());
- EXPECT_STREQ(
- "|||||S||",
- SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str());
- EXPECT_STREQ(
"|||B||||",
SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str());
EXPECT_STREQ(
- "||D|||||",
+ "||DJ|||||", // note: DJ alias
SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str());
- EXPECT_STREQ(
- "||J|||||",
- SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str());
- EXPECT_STREQ(
- "||F|||||",
- SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str());
- EXPECT_STREQ(
- "||I|||||",
- SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str());
SideEffects s = SideEffects::None();
s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false));
s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false));
s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false));
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat));
s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble));
- EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str());
+ EXPECT_STREQ(
+ "||DFJI|FI||S|DJC|", // note: DJ/FI alias.
+ s.ToString().c_str());
}
} // namespace art
}
//
- // Misc. tests on false cross-over loops with data types (I/F and J/D) that used
- // to be aliased in an older version of the compiler. This alias has been removed,
- // however, which enables hoisting the invariant array reference.
- //
-
- /// CHECK-START: void Main.FalseCrossOverLoop1() licm (before)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.FalseCrossOverLoop1() licm (after)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:none
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void FalseCrossOverLoop1() {
- sArrF[20] = -1;
- for (int i = 0; i < sArrI.length; i++) {
- sArrI[i] = (int) sArrF[20] - 2;
- }
- }
-
- /// CHECK-START: void Main.FalseCrossOverLoop2() licm (before)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.FalseCrossOverLoop2() licm (after)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:none
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void FalseCrossOverLoop2() {
- sArrI[20] = -2;
- for (int i = 0; i < sArrF.length; i++) {
- sArrF[i] = sArrI[20] - 2;
- }
- }
-
- /// CHECK-START: void Main.FalseCrossOverLoop3() licm (before)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.FalseCrossOverLoop3() licm (after)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:none
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void FalseCrossOverLoop3() {
- sArrD[20] = -3;
- for (int i = 0; i < sArrJ.length; i++) {
- sArrJ[i] = (long) sArrD[20] - 2;
- }
- }
-
- /// CHECK-START: void Main.FalseCrossOverLoop4() licm (before)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:{{B\d+}}
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- /// CHECK-START: void Main.FalseCrossOverLoop4() licm (after)
- /// CHECK-DAG: ArraySet loop:none
- /// CHECK-DAG: ArrayGet loop:none
- /// CHECK-DAG: ArraySet loop:{{B\d+}}
-
- private static void FalseCrossOverLoop4() {
- sArrJ[20] = -4;
- for (int i = 0; i < sArrD.length; i++) {
- sArrD[i] = sArrJ[20] - 2;
- }
- }
-
- //
// Driver and testers.
//
public static void main(String[] args) {
DoStaticTests();
new Main().DoInstanceTests();
- System.out.println("passed");
}
private static void DoStaticTests() {
for (int i = 0; i < sArrL.length; i++) {
expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]);
}
- // Misc. tests.
- FalseCrossOverLoop1();
- for (int i = 0; i < sArrI.length; i++) {
- expectEquals(-3, sArrI[i]);
- }
- FalseCrossOverLoop2();
- for (int i = 0; i < sArrF.length; i++) {
- expectEquals(-4, sArrF[i]);
- }
- FalseCrossOverLoop3();
- for (int i = 0; i < sArrJ.length; i++) {
- expectEquals(-5, sArrJ[i]);
- }
- FalseCrossOverLoop4();
- for (int i = 0; i < sArrD.length; i++) {
- expectEquals(-6, sArrD[i]);
- }
}
private void DoInstanceTests() {