summaryrefslogtreecommitdiff
path: root/compiler/optimizing/nodes.h
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing/nodes.h')
-rw-r--r--compiler/optimizing/nodes.h241
1 files changed, 209 insertions, 32 deletions
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 711a6c1b2d..455f4e338d 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -26,6 +26,7 @@
#include "base/arena_object.h"
#include "base/stl_util.h"
#include "dex/compiler_enums.h"
+#include "dex_file.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "handle.h"
#include "handle_scope.h"
@@ -85,6 +86,16 @@ static constexpr InvokeType kInvalidInvokeType = static_cast<InvokeType>(-1);
static constexpr uint32_t kNoDexPc = -1;
+inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
+ // For the purposes of the compiler, the dex files must actually be the same object
+ // if we want to safely treat them as the same. This is especially important for JIT
+ // as custom class loaders can open the same underlying file (or memory) multiple
+ // times and provide different class resolution but no two class loaders should ever
+ // use the same DexFile object - doing so is an unsupported hack that can lead to
+ // all sorts of weird failures.
+ return &lhs == &rhs;
+}
+
enum IfCondition {
// All types.
kCondEQ, // ==
@@ -1920,6 +1931,14 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
environment_ = environment;
}
+ void InsertRawEnvironment(HEnvironment* environment) {
+ DCHECK(environment_ != nullptr);
+ DCHECK_EQ(environment->GetHolder(), this);
+ DCHECK(environment->GetParent() == nullptr);
+ environment->parent_ = environment_;
+ environment_ = environment;
+ }
+
void RemoveEnvironment();
// Set the environment of this instruction, copying it from `environment`. While
@@ -5079,8 +5098,13 @@ class HInstanceFieldSet FINAL : public HTemplateInstruction<2> {
class HArrayGet FINAL : public HExpression<2> {
public:
- HArrayGet(HInstruction* array, HInstruction* index, Primitive::Type type, uint32_t dex_pc)
+ HArrayGet(HInstruction* array,
+ HInstruction* index,
+ Primitive::Type type,
+ uint32_t dex_pc,
+ bool is_string_char_at = false)
: HExpression(type, SideEffects::ArrayReadOfType(type), dex_pc) {
+ SetPackedFlag<kFlagIsStringCharAt>(is_string_char_at);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
}
@@ -5114,12 +5138,24 @@ class HArrayGet FINAL : public HExpression<2> {
return result;
}
+ bool IsStringCharAt() const { return GetPackedFlag<kFlagIsStringCharAt>(); }
+
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
DECLARE_INSTRUCTION(ArrayGet);
private:
+ // We treat a String as an array, creating the HArrayGet from String.charAt()
+ // intrinsic in the instruction simplifier. We can always determine whether
+ // a particular HArrayGet is actually a String.charAt() by looking at the type
+ // of the input but that requires holding the mutator lock, so we prefer to use
+ // a flag, so that code generators don't need to do the locking.
+ static constexpr size_t kFlagIsStringCharAt = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfArrayGetPackedBits = kFlagIsStringCharAt + 1;
+ static_assert(kNumberOfArrayGetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
DISALLOW_COPY_AND_ASSIGN(HArrayGet);
};
@@ -5225,8 +5261,9 @@ class HArraySet FINAL : public HTemplateInstruction<3> {
class HArrayLength FINAL : public HExpression<1> {
public:
- HArrayLength(HInstruction* array, uint32_t dex_pc)
+ HArrayLength(HInstruction* array, uint32_t dex_pc, bool is_string_length = false)
: HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+ SetPackedFlag<kFlagIsStringLength>(is_string_length);
// Note that arrays do not change length, so the instruction does not
// depend on any write.
SetRawInputAt(0, array);
@@ -5240,7 +5277,6 @@ class HArrayLength FINAL : public HExpression<1> {
return obj == InputAt(0);
}
- void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); }
bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); }
DECLARE_INSTRUCTION(ArrayLength);
@@ -5263,8 +5299,12 @@ class HBoundsCheck FINAL : public HExpression<2> {
public:
// `HBoundsCheck` can trigger GC, as it may call the `IndexOutOfBoundsException`
// constructor.
- HBoundsCheck(HInstruction* index, HInstruction* length, uint32_t dex_pc)
- : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc) {
+ HBoundsCheck(HInstruction* index,
+ HInstruction* length,
+ uint32_t dex_pc,
+ uint32_t string_char_at_method_index = DexFile::kDexNoIndex)
+ : HExpression(index->GetType(), SideEffects::CanTriggerGC(), dex_pc),
+ string_char_at_method_index_(string_char_at_method_index) {
DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(index->GetType()));
SetRawInputAt(0, index);
SetRawInputAt(1, length);
@@ -5279,11 +5319,23 @@ class HBoundsCheck FINAL : public HExpression<2> {
bool CanThrow() const OVERRIDE { return true; }
+ bool IsStringCharAt() const { return GetStringCharAtMethodIndex() != DexFile::kDexNoIndex; }
+ uint32_t GetStringCharAtMethodIndex() const { return string_char_at_method_index_; }
+
HInstruction* GetIndex() const { return InputAt(0); }
DECLARE_INSTRUCTION(BoundsCheck);
private:
+ // We treat a String as an array, creating the HBoundsCheck from String.charAt()
+ // intrinsic in the instruction simplifier. We want to include the String.charAt()
+ // in the stack trace if we actually throw the StringIndexOutOfBoundsException,
+ // so we need to create an HEnvironment which will be translated to an InlineInfo
+ // indicating the extra stack frame. Since we add this HEnvironment quite late,
+ // in the PrepareForRegisterAllocation pass, we need to remember the method index
+ // from the invoke as we don't want to look again at the dex bytecode.
+ uint32_t string_char_at_method_index_; // DexFile::kDexNoIndex if regular array.
+
DISALLOW_COPY_AND_ASSIGN(HBoundsCheck);
};
@@ -5329,8 +5381,44 @@ class HNativeDebugInfo : public HTemplateInstruction<0> {
/**
* Instruction to load a Class object.
*/
-class HLoadClass FINAL : public HExpression<1> {
+class HLoadClass FINAL : public HInstruction {
public:
+ // Determines how to load the Class.
+ enum class LoadKind {
+ // Use the Class* from the method's own ArtMethod*.
+ kReferrersClass,
+
+ // Use boot image Class* address that will be known at link time.
+ // Used for boot image classes referenced by boot image code in non-PIC mode.
+ kBootImageLinkTimeAddress,
+
+ // Use PC-relative boot image Class* address that will be known at link time.
+ // Used for boot image classes referenced by boot image code in PIC mode.
+ kBootImageLinkTimePcRelative,
+
+ // Use a known boot image Class* address, embedded in the code by the codegen.
+ // Used for boot image classes referenced by apps in AOT- and JIT-compiled code.
+ // Note: codegen needs to emit a linker patch if indicated by compiler options'
+ // GetIncludePatchInformation().
+ kBootImageAddress,
+
+ // Load from the resolved types array at an absolute address.
+ // Used for classes outside the boot image referenced by JIT-compiled code.
+ kDexCacheAddress,
+
+ // Load from resolved types array in the dex cache using a PC-relative load.
+ // Used for classes outside boot image when we know that we can access
+ // the dex cache arrays using a PC-relative load.
+ kDexCachePcRelative,
+
+ // Load from resolved types array accessed through the class loaded from
+ // the compiled method's own ArtMethod*. This is the default access type when
+ // all other types are unavailable.
+ kDexCacheViaMethod,
+
+ kLast = kDexCacheViaMethod
+ };
+
HLoadClass(HCurrentMethod* current_method,
uint16_t type_index,
const DexFile& dex_file,
@@ -5338,7 +5426,8 @@ class HLoadClass FINAL : public HExpression<1> {
uint32_t dex_pc,
bool needs_access_check,
bool is_in_dex_cache)
- : HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
+ : HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
+ special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
dex_file_(dex_file),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
@@ -5346,26 +5435,47 @@ class HLoadClass FINAL : public HExpression<1> {
// methods so we can't possibly end up in this situation.
DCHECK(!is_referrers_class || !needs_access_check);
- SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class);
+ SetPackedField<LoadKindField>(
+ is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
- SetRawInputAt(0, current_method);
}
- bool CanBeMoved() const OVERRIDE { return true; }
+ void SetLoadKindWithAddress(LoadKind load_kind, uint64_t address) {
+ DCHECK(HasAddress(load_kind));
+ load_data_.address = address;
+ SetLoadKindInternal(load_kind);
+ }
- bool InstructionDataEquals(const HInstruction* other) const OVERRIDE {
- // Note that we don't need to test for generate_clinit_check_.
- // Whether or not we need to generate the clinit check is processed in
- // prepare_for_register_allocator based on existing HInvokes and HClinitChecks.
- return other->AsLoadClass()->type_index_ == type_index_ &&
- other->AsLoadClass()->GetPackedFields() == GetPackedFields();
+ void SetLoadKindWithTypeReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t type_index) {
+ DCHECK(HasTypeReference(load_kind));
+ DCHECK(IsSameDexFile(dex_file_, dex_file));
+ DCHECK_EQ(type_index_, type_index);
+ SetLoadKindInternal(load_kind);
}
+ void SetLoadKindWithDexCacheReference(LoadKind load_kind,
+ const DexFile& dex_file,
+ uint32_t element_index) {
+ DCHECK(HasDexCacheReference(load_kind));
+ DCHECK(IsSameDexFile(dex_file_, dex_file));
+ load_data_.dex_cache_element_index = element_index;
+ SetLoadKindInternal(load_kind);
+ }
+
+ LoadKind GetLoadKind() const {
+ return GetPackedField<LoadKindField>();
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+
+ bool InstructionDataEquals(const HInstruction* other) const;
+
size_t ComputeHashCode() const OVERRIDE { return type_index_; }
- uint16_t GetTypeIndex() const { return type_index_; }
bool CanBeNull() const OVERRIDE { return false; }
bool NeedsEnvironment() const OVERRIDE {
@@ -5400,7 +5510,15 @@ class HLoadClass FINAL : public HExpression<1> {
loaded_class_rti_ = rti;
}
- const DexFile& GetDexFile() { return dex_file_; }
+ uint32_t GetTypeIndex() const { return type_index_; }
+ const DexFile& GetDexFile() const { return dex_file_; }
+
+ uint32_t GetDexCacheElementOffset() const;
+
+ uint64_t GetAddress() const {
+ DCHECK(HasAddress(GetLoadKind()));
+ return load_data_.address;
+ }
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); }
@@ -5408,30 +5526,96 @@ class HLoadClass FINAL : public HExpression<1> {
return SideEffects::CanTriggerGC();
}
- bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); }
+ bool IsReferrersClass() const { return GetLoadKind() == LoadKind::kReferrersClass; }
bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
+ void MarkInDexCache() {
+ SetPackedFlag<kFlagIsInDexCache>(true);
+ DCHECK(!NeedsEnvironment());
+ RemoveEnvironment();
+ SetSideEffects(SideEffects::None());
+ }
+
+ void AddSpecialInput(HInstruction* special_input);
+
+ using HInstruction::GetInputRecords; // Keep the const version visible.
+ ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
+ return ArrayRef<HUserRecord<HInstruction*>>(
+ &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
+ }
+
+ Primitive::Type GetType() const OVERRIDE {
+ return Primitive::kPrimNot;
+ }
+
DECLARE_INSTRUCTION(LoadClass);
private:
- static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits;
- static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1;
+ static constexpr size_t kFlagNeedsAccessCheck = kNumberOfGenericPackedBits;
static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1;
// Whether this instruction must generate the initialization check.
// Used for code generation.
static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
- static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1;
+ static constexpr size_t kFieldLoadKind = kFlagGenerateClInitCheck + 1;
+ static constexpr size_t kFieldLoadKindSize =
+ MinimumBitsToStore(static_cast<size_t>(LoadKind::kLast));
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFieldLoadKind + kFieldLoadKindSize;
static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
+ using LoadKindField = BitField<LoadKind, kFieldLoadKind, kFieldLoadKindSize>;
+
+ static bool HasTypeReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageLinkTimeAddress ||
+ load_kind == LoadKind::kBootImageLinkTimePcRelative ||
+ load_kind == LoadKind::kDexCacheViaMethod ||
+ load_kind == LoadKind::kReferrersClass;
+ }
+
+ static bool HasAddress(LoadKind load_kind) {
+ return load_kind == LoadKind::kBootImageAddress || load_kind == LoadKind::kDexCacheAddress;
+ }
+
+ static bool HasDexCacheReference(LoadKind load_kind) {
+ return load_kind == LoadKind::kDexCachePcRelative;
+ }
+
+ void SetLoadKindInternal(LoadKind load_kind);
+
+ // The special input is the HCurrentMethod for kDexCacheViaMethod or kReferrersClass.
+ // For other load kinds it's empty or possibly some architecture-specific instruction
+ // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
+ HUserRecord<HInstruction*> special_input_;
const uint16_t type_index_;
const DexFile& dex_file_;
+ union {
+ uint32_t dex_cache_element_index; // Only for dex cache reference.
+ uint64_t address; // Up to 64-bit, needed for kDexCacheAddress on 64-bit targets.
+ } load_data_;
+
ReferenceTypeInfo loaded_class_rti_;
DISALLOW_COPY_AND_ASSIGN(HLoadClass);
};
+std::ostream& operator<<(std::ostream& os, HLoadClass::LoadKind rhs);
+
+// Note: defined outside class to see operator<<(., HLoadClass::LoadKind).
+inline uint32_t HLoadClass::GetDexCacheElementOffset() const {
+ DCHECK(HasDexCacheReference(GetLoadKind())) << GetLoadKind();
+ return load_data_.dex_cache_element_index;
+}
+
+// Note: defined outside class to see operator<<(., HLoadClass::LoadKind).
+inline void HLoadClass::AddSpecialInput(HInstruction* special_input) {
+ // The special input is used for PC-relative loads on some architectures.
+ DCHECK(GetLoadKind() == LoadKind::kBootImageLinkTimePcRelative ||
+ GetLoadKind() == LoadKind::kDexCachePcRelative) << GetLoadKind();
+ DCHECK(special_input_.GetInstruction() == nullptr);
+ special_input_ = HUserRecord<HInstruction*>(special_input);
+ special_input->AddUseAt(this, 0);
+}
class HLoadString FINAL : public HInstruction {
public:
@@ -5599,6 +5783,9 @@ class HLoadString FINAL : public HInstruction {
void SetLoadKindInternal(LoadKind load_kind);
+ // The special input is the HCurrentMethod for kDexCacheViaMethod.
+ // For other load kinds it's empty or possibly some architecture-specific instruction
+ // for PC-relative loads, i.e. kDexCachePcRelative or kBootImageLinkTimePcRelative.
HUserRecord<HInstruction*> special_input_;
// String index serves also as the hash code and it's also needed for slow-paths,
@@ -6572,16 +6759,6 @@ inline int64_t Int64FromConstant(HConstant* constant) {
}
}
-inline bool IsSameDexFile(const DexFile& lhs, const DexFile& rhs) {
- // For the purposes of the compiler, the dex files must actually be the same object
- // if we want to safely treat them as the same. This is especially important for JIT
- // as custom class loaders can open the same underlying file (or memory) multiple
- // times and provide different class resolution but no two class loaders should ever
- // use the same DexFile object - doing so is an unsupported hack that can lead to
- // all sorts of weird failures.
- return &lhs == &rhs;
-}
-
#define INSTRUCTION_TYPE_CHECK(type, super) \
inline bool HInstruction::Is##type() const { return GetKind() == k##type; } \
inline const H##type* HInstruction::As##type() const { \