summaryrefslogtreecommitdiffstats
path: root/compiler
diff options
context:
space:
mode:
authorNicolas Geoffray <ngeoffray@google.com>2014-10-22 16:07:05 +0100
committerNicolas Geoffray <ngeoffray@google.com>2014-10-29 13:01:48 +0000
commit19a19cffd197a28ae4c9c3e59eff6352fd392241 (patch)
tree265b971afd0e33afc8986317aea2f5a6fe817aec /compiler
parent7c049c1f34220b0dc1a7f68f3b30f388bae7bdb9 (diff)
downloadart-19a19cffd197a28ae4c9c3e59eff6352fd392241.zip
art-19a19cffd197a28ae4c9c3e59eff6352fd392241.tar.gz
art-19a19cffd197a28ae4c9c3e59eff6352fd392241.tar.bz2
Add support for static fields in optimizing compiler.
Change-Id: Id2f010589e2bd6faf42c05bb33abf6816ebe9fa9
Diffstat (limited to 'compiler')
-rw-r--r--compiler/driver/compiler_driver.cc5
-rw-r--r--compiler/driver/compiler_driver.h3
-rw-r--r--compiler/optimizing/builder.cc98
-rw-r--r--compiler/optimizing/builder.h7
-rw-r--r--compiler/optimizing/code_generator.cc7
-rw-r--r--compiler/optimizing/code_generator.h5
-rw-r--r--compiler/optimizing/code_generator_arm.cc277
-rw-r--r--compiler/optimizing/code_generator_arm.h7
-rw-r--r--compiler/optimizing/code_generator_arm64.cc6
-rw-r--r--compiler/optimizing/code_generator_x86.cc221
-rw-r--r--compiler/optimizing/code_generator_x86.h4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc209
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/locations.h9
-rw-r--r--compiler/optimizing/nodes.h130
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc4
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.h1
-rw-r--r--compiler/utils/arm/assembler_arm.h3
-rw-r--r--compiler/utils/arm/assembler_arm32.cc7
-rw-r--r--compiler/utils/arm/assembler_arm32.h2
-rw-r--r--compiler/utils/arm/assembler_thumb2.cc9
-rw-r--r--compiler/utils/arm/assembler_thumb2.h3
-rw-r--r--compiler/utils/arm/constants_arm.h10
23 files changed, 952 insertions, 79 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index eb5739f..05785a8 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1071,7 +1071,8 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi
bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit,
bool is_put, MemberOffset* field_offset,
uint32_t* storage_index, bool* is_referrers_class,
- bool* is_volatile, bool* is_initialized) {
+ bool* is_volatile, bool* is_initialized,
+ Primitive::Type* type) {
ScopedObjectAccess soa(Thread::Current());
// Try to resolve the field and compiling method's class.
mirror::ArtField* resolved_field;
@@ -1104,6 +1105,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
*is_initialized = (*is_referrers_class) ||
(IsStaticFieldsClassInitialized(referrer_class, resolved_field) &&
CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index));
+ *type = resolved_field->GetTypeAsPrimitiveType();
} else {
// Conservative defaults.
*is_volatile = true;
@@ -1111,6 +1113,7 @@ bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompila
*storage_index = -1;
*is_referrers_class = false;
*is_initialized = false;
+ *type = Primitive::kPrimVoid;
}
ProcessedStaticField(result, *is_referrers_class);
return result;
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 6764aef..65a842d 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -311,7 +311,8 @@ class CompilerDriver {
// field is within the referrer (which can avoid checking class initialization).
bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put,
MemberOffset* field_offset, uint32_t* storage_index,
- bool* is_referrers_class, bool* is_volatile, bool* is_initialized)
+ bool* is_referrers_class, bool* is_volatile, bool* is_initialized,
+ Primitive::Type* type)
LOCKS_EXCLUDED(Locks::mutator_lock_);
// Can we fastpath a interface, super class or virtual method call? Computes method's vtable
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index f80ebdb..e4ccd96 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -1,5 +1,4 @@
/*
- *
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -421,9 +420,9 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction,
return true;
}
-bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
- uint32_t dex_offset,
- bool is_put) {
+bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction,
+ uint32_t dex_offset,
+ bool is_put) {
uint32_t source_or_dest_reg = instruction.VRegA_22c();
uint32_t obj_reg = instruction.VRegB_22c();
uint16_t field_index = instruction.VRegC_22c();
@@ -469,6 +468,67 @@ bool HGraphBuilder::BuildFieldAccess(const Instruction& instruction,
return true;
}
+
+
+bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction,
+ uint32_t dex_offset,
+ bool is_put) {
+ uint32_t source_or_dest_reg = instruction.VRegA_21c();
+ uint16_t field_index = instruction.VRegB_21c();
+
+ uint32_t storage_index;
+ bool is_referrers_class;
+ bool is_initialized;
+ bool is_volatile;
+ MemberOffset field_offset(0u);
+ Primitive::Type field_type;
+
+ bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index,
+ dex_compilation_unit_,
+ is_put,
+ &field_offset,
+ &storage_index,
+ &is_referrers_class,
+ &is_volatile,
+ &is_initialized,
+ &field_type);
+ if (!fast_path) {
+ return false;
+ }
+
+ if (is_volatile) {
+ return false;
+ }
+
+ if (!IsTypeSupported(field_type)) {
+ return false;
+ }
+
+ HLoadClass* constant = new (arena_) HLoadClass(
+ storage_index, is_referrers_class, is_initialized, dex_offset);
+ current_block_->AddInstruction(constant);
+
+ HInstruction* cls = constant;
+ if (constant->NeedsInitialization()) {
+ cls = new (arena_) HClinitCheck(constant, dex_offset);
+ current_block_->AddInstruction(cls);
+ }
+
+ if (is_put) {
+ // We need to keep the class alive before loading the value.
+ Temporaries temps(graph_, 1);
+ temps.Add(cls);
+ HInstruction* value = LoadLocal(source_or_dest_reg, field_type);
+ DCHECK_EQ(value->GetType(), field_type);
+ current_block_->AddInstruction(
+ new (arena_) HStaticFieldSet(cls, value, field_type, field_offset));
+ } else {
+ current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset));
+ UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction());
+ }
+ return true;
+}
+
void HGraphBuilder::BuildArrayAccess(const Instruction& instruction,
uint32_t dex_offset,
bool is_put,
@@ -1043,7 +1103,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::IGET_BYTE:
case Instruction::IGET_CHAR:
case Instruction::IGET_SHORT: {
- if (!BuildFieldAccess(instruction, dex_offset, false)) {
+ if (!BuildInstanceFieldAccess(instruction, dex_offset, false)) {
return false;
}
break;
@@ -1056,7 +1116,33 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::IPUT_BYTE:
case Instruction::IPUT_CHAR:
case Instruction::IPUT_SHORT: {
- if (!BuildFieldAccess(instruction, dex_offset, true)) {
+ if (!BuildInstanceFieldAccess(instruction, dex_offset, true)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SGET:
+ case Instruction::SGET_WIDE:
+ case Instruction::SGET_OBJECT:
+ case Instruction::SGET_BOOLEAN:
+ case Instruction::SGET_BYTE:
+ case Instruction::SGET_CHAR:
+ case Instruction::SGET_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_offset, false)) {
+ return false;
+ }
+ break;
+ }
+
+ case Instruction::SPUT:
+ case Instruction::SPUT_WIDE:
+ case Instruction::SPUT_OBJECT:
+ case Instruction::SPUT_BOOLEAN:
+ case Instruction::SPUT_BYTE:
+ case Instruction::SPUT_CHAR:
+ case Instruction::SPUT_SHORT: {
+ if (!BuildStaticFieldAccess(instruction, dex_offset, true)) {
return false;
}
break;
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index b55ef07..eea762f 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -118,7 +118,12 @@ class HGraphBuilder : public ValueObject {
void BuildReturn(const Instruction& instruction, Primitive::Type type);
- bool BuildFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_get);
+ // Builds an instance field access node and returns whether the instruction is supported.
+ bool BuildInstanceFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put);
+
+ // Builds a static field access node and returns whether the instruction is supported.
+ bool BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_offset, bool is_put);
+
void BuildArrayAccess(const Instruction& instruction,
uint32_t dex_offset,
bool is_get,
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 28ff1cf..c61e991 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -26,6 +26,9 @@
#include "gc_map_builder.h"
#include "leb128.h"
#include "mapping_table.h"
+#include "mirror/array-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object_reference.h"
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
#include "verifier/dex_gc_map.h"
@@ -33,6 +36,10 @@
namespace art {
+size_t CodeGenerator::GetCacheOffset(uint32_t index) {
+ return mirror::ObjectArray<mirror::Object>::OffsetOfElement(index).SizeValue();
+}
+
void CodeGenerator::CompileBaseline(CodeAllocator* allocator, bool is_leaf) {
const GrowableArray<HBasicBlock*>& blocks = GetGraph()->GetBlocks();
DCHECK(blocks.Get(0) == GetGraph()->GetEntryBlock());
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 1638869..bf9d2c0 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -157,6 +157,11 @@ class CodeGenerator : public ArenaObject {
bool* GetBlockedCoreRegisters() const { return blocked_core_registers_; }
bool* GetBlockedFloatingPointRegisters() const { return blocked_fpu_registers_; }
+ // Helper that returns the pointer offset of an index in an object array.
+ // Note: this method assumes we always have the same pointer size, regardless
+ // of the architecture.
+ static size_t GetCacheOffset(uint32_t index);
+
protected:
CodeGenerator(HGraph* graph,
size_t number_of_core_registers,
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index a5d4c43..a06860a 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -60,6 +60,7 @@ class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegis
};
#define __ reinterpret_cast<ArmAssembler*>(codegen->GetAssembler())->
+#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmWordSize, x).Int32Value()
class SlowPathCodeARM : public SlowPathCode {
public:
@@ -80,11 +81,10 @@ class NullCheckSlowPathARM : public SlowPathCodeARM {
explicit NullCheckSlowPathARM(HNullCheck* instruction) : instruction_(instruction) {}
virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowNullPointer).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ arm_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pThrowNullPointer), instruction_, instruction_->GetDexPc());
}
private:
@@ -115,10 +115,8 @@ class SuspendCheckSlowPathARM : public SlowPathCodeARM {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
codegen->SaveLiveRegisters(instruction_->GetLocations());
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pTestSuspend).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ arm_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pTestSuspend), instruction_, instruction_->GetDexPc());
codegen->RestoreLiveRegisters(instruction_->GetLocations());
if (successor_ == nullptr) {
__ b(GetReturnLabel());
@@ -156,12 +154,12 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM {
CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
__ Bind(GetEntryLabel());
InvokeRuntimeCallingConvention calling_convention;
- arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
- arm_codegen->Move32(Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pThrowArrayBounds).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
- codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ arm_codegen->Move32(
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
+ arm_codegen->Move32(
+ Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+ arm_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pThrowArrayBounds), instruction_, instruction_->GetDexPc());
}
private:
@@ -172,6 +170,32 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM {
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM);
};
+class ClinitCheckSlowPathARM : public SlowPathCodeARM {
+ public:
+ explicit ClinitCheckSlowPathARM(HClinitCheck* instruction) : instruction_(instruction) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen);
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+
+ HLoadClass* cls = instruction_->GetLoadClass();
+ InvokeRuntimeCallingConvention calling_convention;
+ __ LoadImmediate(calling_convention.GetRegisterAt(0), cls->GetTypeIndex());
+ arm_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ arm_codegen->InvokeRuntime(
+ QUICK_ENTRY_POINT(pInitializeStaticStorage), instruction_, instruction_->GetDexPc());
+ arm_codegen->Move32(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(R0));
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ __ b(GetExitLabel());
+ }
+
+ private:
+ HClinitCheck* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathARM);
+};
+
#undef __
#define __ reinterpret_cast<ArmAssembler*>(GetAssembler())->
@@ -684,6 +708,18 @@ void CodeGeneratorARM::Move(HInstruction* instruction, Location location, HInstr
}
}
+void CodeGeneratorARM::InvokeRuntime(int32_t entry_point_offset,
+ HInstruction* instruction,
+ uint32_t dex_pc) {
+ __ LoadFromOffset(kLoadWord, LR, TR, entry_point_offset);
+ __ blx(LR);
+ RecordPcInfo(instruction, dex_pc);
+ DCHECK(instruction->IsSuspendCheck()
+ || instruction->IsBoundsCheck()
+ || instruction->IsNullCheck()
+ || !IsLeafMethod());
+}
+
void LocationsBuilderARM::VisitGoto(HGoto* got) {
got->SetLocations(nullptr);
}
@@ -971,15 +1007,12 @@ void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) {
HandleInvoke(invoke);
}
-void InstructionCodeGeneratorARM::LoadCurrentMethod(Register reg) {
+void CodeGeneratorARM::LoadCurrentMethod(Register reg) {
__ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset);
}
void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
- uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
- size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
- invoke->GetIndexInDexCache() * kArmWordSize;
// TODO: Implement all kinds of calls:
// 1) boot -> boot
@@ -989,11 +1022,13 @@ void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) {
// Currently we implement the app -> app logic, which looks up in the resolve cache.
// temp = method;
- LoadCurrentMethod(temp);
+ codegen_->LoadCurrentMethod(temp);
// temp = temp->dex_cache_resolved_methods_;
- __ LoadFromOffset(kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
+ __ LoadFromOffset(
+ kLoadWord, temp, temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value());
// temp = temp[index_in_cache]
- __ LoadFromOffset(kLoadWord, temp, temp, index_in_cache);
+ __ LoadFromOffset(
+ kLoadWord, temp, temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache()));
// LR = temp[offset_of_quick_compiled_code]
__ LoadFromOffset(kLoadWord, LR, temp,
mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value());
@@ -1392,15 +1427,10 @@ void LocationsBuilderARM::VisitNewInstance(HNewInstance* instruction) {
void InstructionCodeGeneratorARM::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocObjectWithAccessCheck).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
-
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(
+ QUICK_ENTRY_POINT(pAllocObjectWithAccessCheck), instruction, instruction->GetDexPc());
}
void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
@@ -1415,15 +1445,10 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) {
void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex());
-
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAllocArrayWithAccessCheck).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
-
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(
+ QUICK_ENTRY_POINT(pAllocArrayWithAccessCheck), instruction, instruction->GetDexPc());
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
@@ -1850,11 +1875,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) {
}
case Primitive::kPrimNot: {
- int32_t offset = QUICK_ENTRYPOINT_OFFSET(kArmWordSize, pAputObject).Int32Value();
- __ LoadFromOffset(kLoadWord, LR, TR, offset);
- __ blx(LR);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
- DCHECK(!codegen_->IsLeafMethod());
+ codegen_->InvokeRuntime(QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc());
break;
}
@@ -2065,5 +2086,177 @@ void ParallelMoveResolverARM::RestoreScratch(int reg) {
__ Pop(static_cast<Register>(reg));
}
+void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) {
+ Register out = cls->GetLocations()->Out().As<Register>();
+ if (cls->IsReferrersClass()) {
+ codegen_->LoadCurrentMethod(out);
+ __ LoadFromOffset(kLoadWord, out, out, mirror::ArtMethod::DeclaringClassOffset().Int32Value());
+ } else {
+ codegen_->LoadCurrentMethod(out);
+ __ LoadFromOffset(
+ kLoadWord, out, out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value());
+ __ LoadFromOffset(kLoadWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex()));
+ }
+}
+
+void LocationsBuilderARM::VisitClinitCheck(HClinitCheck* check) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (check->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorARM::VisitClinitCheck(HClinitCheck* check) {
+ SlowPathCodeARM* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathARM(check);
+ codegen_->AddSlowPath(slow_path);
+
+ LocationSummary* locations = check->GetLocations();
+ // We remove the class as a live register, we know it's null or unused in the slow path.
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ register_set->Remove(locations->InAt(0));
+
+ Register class_reg = locations->InAt(0).As<Register>();
+ __ cmp(class_reg, ShifterOperand(0));
+ __ b(slow_path->GetEntryLabel(), EQ);
+ __ LoadFromOffset(kLoadWord, IP, class_reg, mirror::Class::StatusOffset().Int32Value());
+ __ cmp(IP, ShifterOperand(mirror::Class::kStatusInitialized));
+ __ b(slow_path->GetEntryLabel(), LT);
+ // Even if the initialized flag is set, we may be in a situation where caches are not synced
+ // properly. Therefore, we do a memory fence.
+ __ dmb(ISH);
+ __ Bind(slow_path->GetExitLabel());
+}
+
+void LocationsBuilderARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register cls = locations->InAt(0).As<Register>();
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ Register out = locations->Out().As<Register>();
+ __ LoadFromOffset(kLoadUnsignedByte, out, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ Register out = locations->Out().As<Register>();
+ __ LoadFromOffset(kLoadSignedByte, out, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ Register out = locations->Out().As<Register>();
+ __ LoadFromOffset(kLoadSignedHalfword, out, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ Register out = locations->Out().As<Register>();
+ __ LoadFromOffset(kLoadUnsignedHalfword, out, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ Register out = locations->Out().As<Register>();
+ __ LoadFromOffset(kLoadWord, out, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ // TODO: support volatile.
+ Location out = locations->Out();
+ __ LoadFromOffset(kLoadWordPair, out.AsRegisterPairLow<Register>(), cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ bool is_object_type = instruction->GetFieldType() == Primitive::kPrimNot;
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ // Temporary registers for the write barrier.
+ if (is_object_type) {
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+}
+
+void InstructionCodeGeneratorARM::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register cls = locations->InAt(0).As<Register>();
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+ Primitive::Type field_type = instruction->GetFieldType();
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ Register value = locations->InAt(1).As<Register>();
+ __ StoreToOffset(kStoreByte, value, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ Register value = locations->InAt(1).As<Register>();
+ __ StoreToOffset(kStoreHalfword, value, cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ Register value = locations->InAt(1).As<Register>();
+ __ StoreToOffset(kStoreWord, value, cls, offset);
+ if (field_type == Primitive::kPrimNot) {
+ Register temp = locations->GetTemp(0).As<Register>();
+ Register card = locations->GetTemp(1).As<Register>();
+ codegen_->MarkGCCard(temp, card, cls, value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ Location value = locations->InAt(1);
+ __ StoreToOffset(kStoreWordPair, value.AsRegisterPairLow<Register>(), cls, offset);
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << field_type;
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << field_type;
+ UNREACHABLE();
+ }
+}
+
} // namespace arm
} // namespace art
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index b1c36c0..c65b426 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -125,7 +125,6 @@ class InstructionCodeGeneratorARM : public HGraphVisitor {
#undef DECLARE_VISIT_INSTRUCTION
ArmAssembler* GetAssembler() const { return assembler_; }
- void LoadCurrentMethod(Register reg);
private:
// Generate code for the given suspend check. If not null, `successor`
@@ -194,6 +193,12 @@ class CodeGeneratorARM : public CodeGenerator {
// Helper method to move a 64bits value between two locations.
void Move64(Location destination, Location source);
+ // Load current method into `reg`.
+ void LoadCurrentMethod(Register reg);
+
+ // Generate code to invoke a runtime entry point.
+ void InvokeRuntime(int32_t offset, HInstruction* instruction, uint32_t dex_pc);
+
// Emit a write barrier.
void MarkGCCard(Register temp, Register card, Register object, Register value);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index f9aa44b..fe999c2 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -534,13 +534,17 @@ InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
#define FOR_EACH_UNIMPLEMENTED_INSTRUCTION(M) \
M(ArrayGet) \
M(ArraySet) \
+ M(ClinitCheck) \
M(DoubleConstant) \
M(Div) \
M(FloatConstant) \
M(Mul) \
+ M(LoadClass) \
M(Neg) \
M(NewArray) \
- M(ParallelMove)
+ M(ParallelMove) \
+ M(StaticFieldGet) \
+ M(StaticFieldSet) \
#define UNIMPLEMENTED_INSTRUCTION_BREAK_CODE(name) name##UnimplementedInstructionBreakCode
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 495ff8b..267edca 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -157,6 +157,32 @@ class SuspendCheckSlowPathX86 : public SlowPathCodeX86 {
DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathX86);
};
+class ClinitCheckSlowPathX86 : public SlowPathCodeX86 {
+ public:
+ explicit ClinitCheckSlowPathX86(HClinitCheck* instruction) : instruction_(instruction) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen);
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+
+ HLoadClass* cls = instruction_->GetLoadClass();
+ InvokeRuntimeCallingConvention calling_convention;
+ __ movl(calling_convention.GetRegisterAt(0), Immediate(cls->GetTypeIndex()));
+ x86_codegen->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ __ fs()->call(Address::Absolute(QUICK_ENTRYPOINT_OFFSET(kX86WordSize, pInitializeStaticStorage)));
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ x86_codegen->Move32(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(EAX));
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ HClinitCheck* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathX86);
+};
+
#undef __
#define __ reinterpret_cast<X86Assembler*>(GetAssembler())->
@@ -313,7 +339,7 @@ void CodeGeneratorX86::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
-void InstructionCodeGeneratorX86::LoadCurrentMethod(Register reg) {
+void CodeGeneratorX86::LoadCurrentMethod(Register reg) {
__ movl(reg, Address(ESP, kCurrentMethodStackOffset));
}
@@ -897,9 +923,6 @@ void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) {
void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
Register temp = invoke->GetLocations()->GetTemp(0).As<Register>();
- uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
- size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).Int32Value() +
- invoke->GetIndexInDexCache() * kX86WordSize;
// TODO: Implement all kinds of calls:
// 1) boot -> boot
@@ -909,11 +932,11 @@ void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) {
// Currently we implement the app -> app logic, which looks up in the resolve cache.
// temp = method;
- LoadCurrentMethod(temp);
+ codegen_->LoadCurrentMethod(temp);
// temp = temp->dex_cache_resolved_methods_;
__ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().Int32Value()));
// temp = temp[index_in_cache]
- __ movl(temp, Address(temp, index_in_cache));
+ __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().Int32Value()));
@@ -1354,7 +1377,7 @@ void LocationsBuilderX86::VisitNewInstance(HNewInstance* instruction) {
void InstructionCodeGeneratorX86::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
__ fs()->call(
@@ -1376,7 +1399,7 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) {
void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(calling_convention.GetRegisterAt(1));
+ codegen_->LoadCurrentMethod(calling_convention.GetRegisterAt(1));
__ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex()));
__ fs()->call(
@@ -2111,5 +2134,187 @@ void ParallelMoveResolverX86::RestoreScratch(int reg) {
__ popl(static_cast<Register>(reg));
}
+void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) {
+ Register out = cls->GetLocations()->Out().As<Register>();
+ if (cls->IsReferrersClass()) {
+ codegen_->LoadCurrentMethod(out);
+ __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value()));
+ } else {
+ codegen_->LoadCurrentMethod(out);
+ __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ }
+}
+
+void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (check->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitClinitCheck(HClinitCheck* check) {
+ SlowPathCodeX86* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathX86(check);
+ codegen_->AddSlowPath(slow_path);
+
+ LocationSummary* locations = check->GetLocations();
+ // We remove the class as a live register, we know it's null or unused in the slow path.
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ register_set->Remove(locations->InAt(0));
+
+ Register class_reg = locations->InAt(0).As<Register>();
+ __ testl(class_reg, class_reg);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ __ cmpl(Address(class_reg, mirror::Class::StatusOffset().Int32Value()),
+ Immediate(mirror::Class::kStatusInitialized));
+ __ j(kLess, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ // No need for memory fence, thanks to the X86 memory model.
+}
+
+void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register cls = locations->InAt(0).As<Register>();
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ Register out = locations->Out().As<Register>();
+ __ movzxb(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ Register out = locations->Out().As<Register>();
+ __ movsxb(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ Register out = locations->Out().As<Register>();
+ __ movsxw(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ Register out = locations->Out().As<Register>();
+ __ movzxw(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ Register out = locations->Out().As<Register>();
+ __ movl(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ // TODO: support volatile.
+ __ movl(locations->Out().AsRegisterPairLow<Register>(), Address(cls, offset));
+ __ movl(locations->Out().AsRegisterPairHigh<Register>(), Address(cls, kX86WordSize + offset));
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ Primitive::Type field_type = instruction->GetFieldType();
+ bool is_object_type = field_type == Primitive::kPrimNot;
+ bool is_byte_type = (field_type == Primitive::kPrimBoolean)
+ || (field_type == Primitive::kPrimByte);
+ // The register allocator does not support multiple
+ // inputs that die at entry with one in a specific register.
+ if (is_byte_type) {
+ // Ensure the value is in a byte register.
+ locations->SetInAt(1, Location::RegisterLocation(EAX));
+ } else {
+ locations->SetInAt(1, Location::RequiresRegister());
+ }
+ // Temporary registers for the write barrier.
+ if (is_object_type) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ }
+}
+
+void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ Register cls = locations->InAt(0).As<Register>();
+ uint32_t offset = instruction->GetFieldOffset().Uint32Value();
+ Primitive::Type field_type = instruction->GetFieldType();
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ ByteRegister value = locations->InAt(1).As<ByteRegister>();
+ __ movb(Address(cls, offset), value);
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ Register value = locations->InAt(1).As<Register>();
+ __ movw(Address(cls, offset), value);
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ Register value = locations->InAt(1).As<Register>();
+ __ movl(Address(cls, offset), value);
+
+ if (field_type == Primitive::kPrimNot) {
+ Register temp = locations->GetTemp(0).As<Register>();
+ Register card = locations->GetTemp(1).As<Register>();
+ codegen_->MarkGCCard(temp, card, cls, value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ Location value = locations->InAt(1);
+ __ movl(Address(cls, offset), value.AsRegisterPairLow<Register>());
+ __ movl(Address(cls, kX86WordSize + offset), value.AsRegisterPairHigh<Register>());
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << field_type;
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << field_type;
+ UNREACHABLE();
+ }
+}
+
} // namespace x86
} // namespace art
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index fff91d1..bcceaad 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -119,8 +119,6 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor {
#undef DECLARE_VISIT_INSTRUCTION
- void LoadCurrentMethod(Register reg);
-
X86Assembler* GetAssembler() const { return assembler_; }
private:
@@ -193,6 +191,8 @@ class CodeGeneratorX86 : public CodeGenerator {
// Emit a write barrier.
void MarkGCCard(Register temp, Register card, Register object, Register value);
+ void LoadCurrentMethod(Register reg);
+
Label* GetLabelOf(HBasicBlock* block) const {
return block_labels_.GetRawStorage() + block->GetBlockId();
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 4d11a24..e8d34e3 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -151,8 +151,10 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
__ Bind(GetEntryLabel());
InvokeRuntimeCallingConvention calling_convention;
- x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
- x64_codegen->Move(Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
+ x64_codegen->Move(
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)), index_location_);
+ x64_codegen->Move(
+ Location::RegisterLocation(calling_convention.GetRegisterAt(1)), length_location_);
__ gs()->call(Address::Absolute(
QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pThrowArrayBounds), true));
codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
@@ -166,6 +168,34 @@ class BoundsCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathX86_64);
};
+class ClinitCheckSlowPathX86_64 : public SlowPathCodeX86_64 {
+ public:
+ explicit ClinitCheckSlowPathX86_64(HClinitCheck* instruction) : instruction_(instruction) {}
+
+ virtual void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
+ CodeGeneratorX86_64* x64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+ __ Bind(GetEntryLabel());
+ codegen->SaveLiveRegisters(instruction_->GetLocations());
+
+ HLoadClass* cls = instruction_->GetLoadClass();
+ InvokeRuntimeCallingConvention calling_convention;
+ __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(cls->GetTypeIndex()));
+ x64_codegen->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+ __ gs()->call(Address::Absolute(
+ QUICK_ENTRYPOINT_OFFSET(kX86_64WordSize, pInitializeStaticStorage), true));
+
+ codegen->RecordPcInfo(instruction_, instruction_->GetDexPc());
+ x64_codegen->Move(instruction_->GetLocations()->InAt(0), Location::RegisterLocation(RAX));
+ codegen->RestoreLiveRegisters(instruction_->GetLocations());
+ __ jmp(GetExitLabel());
+ }
+
+ private:
+ HClinitCheck* const instruction_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClinitCheckSlowPathX86_64);
+};
+
#undef __
#define __ reinterpret_cast<X86_64Assembler*>(GetAssembler())->
@@ -314,7 +344,7 @@ void CodeGeneratorX86_64::Bind(HBasicBlock* block) {
__ Bind(GetLabelOf(block));
}
-void InstructionCodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
+void CodeGeneratorX86_64::LoadCurrentMethod(CpuRegister reg) {
__ movl(reg, Address(CpuRegister(RSP), kCurrentMethodStackOffset));
}
@@ -888,10 +918,6 @@ void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
CpuRegister temp = invoke->GetLocations()->GetTemp(0).As<CpuRegister>();
- uint32_t heap_reference_size = sizeof(mirror::HeapReference<mirror::Object>);
- size_t index_in_cache = mirror::Array::DataOffset(heap_reference_size).SizeValue() +
- invoke->GetIndexInDexCache() * heap_reference_size;
-
// TODO: Implement all kinds of calls:
// 1) boot -> boot
// 2) app -> boot
@@ -900,11 +926,11 @@ void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) {
// Currently we implement the app -> app logic, which looks up in the resolve cache.
// temp = method;
- LoadCurrentMethod(temp);
+ codegen_->LoadCurrentMethod(temp);
// temp = temp->dex_cache_resolved_methods_;
__ movl(temp, Address(temp, mirror::ArtMethod::DexCacheResolvedMethodsOffset().SizeValue()));
// temp = temp[index_in_cache]
- __ movl(temp, Address(temp, index_in_cache));
+ __ movl(temp, Address(temp, CodeGenerator::GetCacheOffset(invoke->GetIndexInDexCache())));
// (temp + offset_of_quick_compiled_code)()
__ call(Address(temp, mirror::ArtMethod::EntryPointFromQuickCompiledCodeOffset().SizeValue()));
@@ -1279,7 +1305,7 @@ void LocationsBuilderX86_64::VisitNewInstance(HNewInstance* instruction) {
void InstructionCodeGeneratorX86_64::VisitNewInstance(HNewInstance* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+ codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
__ gs()->call(Address::Absolute(
@@ -1301,7 +1327,7 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) {
void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
InvokeRuntimeCallingConvention calling_convention;
- LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
+ codegen_->LoadCurrentMethod(CpuRegister(calling_convention.GetRegisterAt(1)));
__ movq(CpuRegister(calling_convention.GetRegisterAt(0)), Immediate(instruction->GetTypeIndex()));
__ gs()->call(Address::Absolute(
@@ -2083,5 +2109,166 @@ void ParallelMoveResolverX86_64::RestoreScratch(int reg) {
__ popq(CpuRegister(reg));
}
+void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(cls, LocationSummary::kNoCall);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) {
+ CpuRegister out = cls->GetLocations()->Out().As<CpuRegister>();
+ if (cls->IsReferrersClass()) {
+ codegen_->LoadCurrentMethod(out);
+ __ movl(out, Address(out, mirror::ArtMethod::DeclaringClassOffset().Int32Value()));
+ } else {
+ codegen_->LoadCurrentMethod(out);
+ __ movl(out, Address(out, mirror::ArtMethod::DexCacheResolvedTypesOffset().Int32Value()));
+ __ movl(out, Address(out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex())));
+ }
+}
+
+void LocationsBuilderX86_64::VisitClinitCheck(HClinitCheck* check) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
+ locations->SetInAt(0, Location::RequiresRegister());
+ if (check->HasUses()) {
+ locations->SetOut(Location::SameAsFirstInput());
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
+ SlowPathCodeX86_64* slow_path = new (GetGraph()->GetArena()) ClinitCheckSlowPathX86_64(check);
+ codegen_->AddSlowPath(slow_path);
+
+ LocationSummary* locations = check->GetLocations();
+ // We remove the class as a live register, we know it's null or unused in the slow path.
+ RegisterSet* register_set = locations->GetLiveRegisters();
+ register_set->Remove(locations->InAt(0));
+
+ CpuRegister class_reg = locations->InAt(0).As<CpuRegister>();
+ __ testl(class_reg, class_reg);
+ __ j(kEqual, slow_path->GetEntryLabel());
+ __ cmpl(Address(class_reg, mirror::Class::StatusOffset().Int32Value()),
+ Immediate(mirror::Class::kStatusInitialized));
+ __ j(kLess, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ // No need for memory fence, thanks to the X86_64 memory model.
+}
+
+void LocationsBuilderX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorX86_64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ CpuRegister cls = locations->InAt(0).As<CpuRegister>();
+ CpuRegister out = locations->Out().As<CpuRegister>();
+ size_t offset = instruction->GetFieldOffset().SizeValue();
+
+ switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean: {
+ __ movzxb(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimByte: {
+ __ movsxb(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimShort: {
+ __ movsxw(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimChar: {
+ __ movzxw(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ __ movl(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ __ movq(out, Address(cls, offset));
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << instruction->GetType();
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << instruction->GetType();
+ UNREACHABLE();
+ }
+}
+
+void LocationsBuilderX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall);
+ Primitive::Type field_type = instruction->GetFieldType();
+ bool is_object_type = field_type == Primitive::kPrimNot;
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ if (is_object_type) {
+ // Temporary registers for the write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ }
+}
+
+void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
+ LocationSummary* locations = instruction->GetLocations();
+ CpuRegister cls = locations->InAt(0).As<CpuRegister>();
+ CpuRegister value = locations->InAt(1).As<CpuRegister>();
+ size_t offset = instruction->GetFieldOffset().SizeValue();
+ Primitive::Type field_type = instruction->GetFieldType();
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte: {
+ __ movb(Address(cls, offset), value);
+ break;
+ }
+
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar: {
+ __ movw(Address(cls, offset), value);
+ break;
+ }
+
+ case Primitive::kPrimInt:
+ case Primitive::kPrimNot: {
+ __ movl(Address(cls, offset), value);
+ if (field_type == Primitive::kPrimNot) {
+ CpuRegister temp = locations->GetTemp(0).As<CpuRegister>();
+ CpuRegister card = locations->GetTemp(1).As<CpuRegister>();
+ codegen_->MarkGCCard(temp, card, cls, value);
+ }
+ break;
+ }
+
+ case Primitive::kPrimLong: {
+ __ movq(Address(cls, offset), value);
+ break;
+ }
+
+ case Primitive::kPrimFloat:
+ case Primitive::kPrimDouble:
+ LOG(FATAL) << "Unimplemented register type " << field_type;
+ UNREACHABLE();
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable type " << field_type;
+ UNREACHABLE();
+ }
+}
+
} // namespace x86_64
} // namespace art
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index e04a8d8..32d2702 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -123,8 +123,6 @@ class InstructionCodeGeneratorX86_64 : public HGraphVisitor {
#undef DECLARE_VISIT_INSTRUCTION
- void LoadCurrentMethod(CpuRegister reg);
-
X86_64Assembler* GetAssembler() const { return assembler_; }
private:
@@ -192,6 +190,8 @@ class CodeGeneratorX86_64 : public CodeGenerator {
// Helper method to move a value between two locations.
void Move(Location destination, Location source);
+ void LoadCurrentMethod(CpuRegister reg);
+
Label* GetLabelOf(HBasicBlock* block) const {
return block_labels_.GetRawStorage() + block->GetBlockId();
}
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index de8c78d..94aded6 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -360,6 +360,15 @@ class RegisterSet : public ValueObject {
}
}
+ void Remove(Location loc) {
+ if (loc.IsRegister()) {
+ core_registers_ &= ~(1 << loc.reg());
+ } else {
+ DCHECK(loc.IsFpuRegister());
+ floating_point_registers_ &= ~(1 << loc.reg());
+ }
+ }
+
bool ContainsCoreRegister(uint32_t id) {
return Contains(core_registers_, id);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f530708..86c36b8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -472,6 +472,7 @@ class HBasicBlock : public ArenaObject {
M(ArrayLength, Instruction) \
M(ArraySet, Instruction) \
M(BoundsCheck, Instruction) \
+ M(ClinitCheck, Instruction) \
M(Compare, BinaryOperation) \
M(Condition, BinaryOperation) \
M(Div, BinaryOperation) \
@@ -488,6 +489,7 @@ class HBasicBlock : public ArenaObject {
M(IntConstant, Constant) \
M(InvokeStatic, Invoke) \
M(InvokeVirtual, Invoke) \
+ M(LoadClass, Instruction) \
M(LessThan, Condition) \
M(LessThanOrEqual, Condition) \
M(LoadLocal, Instruction) \
@@ -505,6 +507,8 @@ class HBasicBlock : public ArenaObject {
M(Phi, Instruction) \
M(Return, Instruction) \
M(ReturnVoid, Instruction) \
+ M(StaticFieldGet, Instruction) \
+ M(StaticFieldSet, Instruction) \
M(StoreLocal, Instruction) \
M(Sub, BinaryOperation) \
M(SuspendCheck, Instruction) \
@@ -2017,6 +2021,132 @@ class HSuspendCheck : public HTemplateInstruction<0> {
DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
};
+// TODO: Make this class handle the case the load is null (dex cache
+// is null).
+/**
+ * Instruction to load a Class object.
+ */
+class HLoadClass : public HExpression<0> {
+ public:
+ HLoadClass(uint16_t type_index,
+ bool is_referrers_class,
+ bool is_initialized,
+ uint32_t dex_pc)
+ : HExpression(Primitive::kPrimNot, SideEffects::None()),
+ type_index_(type_index),
+ is_referrers_class_(is_referrers_class),
+ is_initialized_(is_initialized),
+ dex_pc_(dex_pc) {}
+
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return other->AsLoadClass()->type_index_ == type_index_;
+ }
+
+ size_t ComputeHashCode() const OVERRIDE { return type_index_; }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+ uint16_t GetTypeIndex() const { return type_index_; }
+
+ bool NeedsInitialization() const {
+ return !is_initialized_ && !is_referrers_class_;
+ }
+
+ bool IsReferrersClass() const { return is_referrers_class_; }
+
+ DECLARE_INSTRUCTION(LoadClass);
+
+ private:
+ const uint16_t type_index_;
+ const bool is_referrers_class_;
+ const bool is_initialized_;
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HLoadClass);
+};
+
+// TODO: Pass this check to HInvokeStatic nodes.
+/**
+ * Performs an initialization check on its Class object input.
+ */
+class HClinitCheck : public HExpression<1> {
+ public:
+ explicit HClinitCheck(HLoadClass* constant, uint32_t dex_pc)
+ : HExpression(Primitive::kPrimNot, SideEffects::All()),
+ dex_pc_(dex_pc) {
+ SetRawInputAt(0, constant);
+ }
+
+ bool NeedsEnvironment() const OVERRIDE {
+ // May call runtime to initialize the class.
+ return true;
+ }
+
+ uint32_t GetDexPc() const { return dex_pc_; }
+
+ HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
+
+ DECLARE_INSTRUCTION(ClinitCheck);
+
+ private:
+ const uint32_t dex_pc_;
+
+ DISALLOW_COPY_AND_ASSIGN(HClinitCheck);
+};
+
+class HStaticFieldGet : public HExpression<1> {
+ public:
+ HStaticFieldGet(HInstruction* cls,
+ Primitive::Type field_type,
+ MemberOffset field_offset)
+ : HExpression(field_type, SideEffects::DependsOnSomething()),
+ field_info_(field_offset, field_type) {
+ SetRawInputAt(0, cls);
+ }
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ size_t other_offset = other->AsStaticFieldGet()->GetFieldOffset().SizeValue();
+ return other_offset == GetFieldOffset().SizeValue();
+ }
+
+ size_t ComputeHashCode() const OVERRIDE {
+ return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue();
+ }
+
+ MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+
+ DECLARE_INSTRUCTION(StaticFieldGet);
+
+ private:
+ const FieldInfo field_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(HStaticFieldGet);
+};
+
+class HStaticFieldSet : public HTemplateInstruction<2> {
+ public:
+ HStaticFieldSet(HInstruction* cls,
+ HInstruction* value,
+ Primitive::Type field_type,
+ MemberOffset field_offset)
+ : HTemplateInstruction(SideEffects::ChangesSomething()),
+ field_info_(field_offset, field_type) {
+ SetRawInputAt(0, cls);
+ SetRawInputAt(1, value);
+ }
+
+ MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); }
+ Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
+
+ DECLARE_INSTRUCTION(StaticFieldSet);
+
+ private:
+ const FieldInfo field_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
+};
+
class MoveOperands : public ArenaObject {
public:
MoveOperands(Location source, Location destination, HInstruction* instruction)
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index a81dc1b..2387141 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -37,6 +37,10 @@ void PrepareForRegisterAllocation::VisitBoundsCheck(HBoundsCheck* check) {
check->ReplaceWith(check->InputAt(0));
}
+void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) {
+ check->ReplaceWith(check->InputAt(0));
+}
+
void PrepareForRegisterAllocation::VisitCondition(HCondition* condition) {
bool needs_materialization = false;
if (!condition->HasOnlyOneUse()) {
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index e86a39b..3e63ecb 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -35,6 +35,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor {
private:
virtual void VisitNullCheck(HNullCheck* check) OVERRIDE;
virtual void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE;
+ virtual void VisitClinitCheck(HClinitCheck* check) OVERRIDE;
virtual void VisitCondition(HCondition* condition) OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(PrepareForRegisterAllocation);
diff --git a/compiler/utils/arm/assembler_arm.h b/compiler/utils/arm/assembler_arm.h
index 14d48b7..c1ed6a2 100644
--- a/compiler/utils/arm/assembler_arm.h
+++ b/compiler/utils/arm/assembler_arm.h
@@ -523,6 +523,9 @@ class ArmAssembler : public Assembler {
virtual void blx(Register rm, Condition cond = AL) = 0;
virtual void bx(Register rm, Condition cond = AL) = 0;
+ // Memory barriers.
+ virtual void dmb(DmbOptions flavor) = 0;
+
void Pad(uint32_t bytes);
// Macros.
diff --git a/compiler/utils/arm/assembler_arm32.cc b/compiler/utils/arm/assembler_arm32.cc
index 3d46617..d262b6a 100644
--- a/compiler/utils/arm/assembler_arm32.cc
+++ b/compiler/utils/arm/assembler_arm32.cc
@@ -1471,9 +1471,14 @@ void Arm32Assembler::StoreDToOffset(DRegister reg,
void Arm32Assembler::MemoryBarrier(ManagedRegister mscratch) {
CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
+ dmb(SY);
+}
+
+
+void Arm32Assembler::dmb(DmbOptions flavor) {
#if ANDROID_SMP != 0
int32_t encoding = 0xf57ff05f; // dmb
- Emit(encoding);
+ Emit(encoding | flavor);
#endif
}
diff --git a/compiler/utils/arm/assembler_arm32.h b/compiler/utils/arm/assembler_arm32.h
index c89fd04..cfc300b 100644
--- a/compiler/utils/arm/assembler_arm32.h
+++ b/compiler/utils/arm/assembler_arm32.h
@@ -228,6 +228,8 @@ class Arm32Assembler FINAL : public ArmAssembler {
void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
+ // Memory barriers.
+ void dmb(DmbOptions flavor) OVERRIDE;
// Macros.
// Add signed constant value to rd. May clobber IP.
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc
index 37478c4..633f55b 100644
--- a/compiler/utils/arm/assembler_thumb2.cc
+++ b/compiler/utils/arm/assembler_thumb2.cc
@@ -2521,9 +2521,14 @@ void Thumb2Assembler::StoreDToOffset(DRegister reg,
void Thumb2Assembler::MemoryBarrier(ManagedRegister mscratch) {
CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
+ dmb(SY);
+}
+
+
+void Thumb2Assembler::dmb(DmbOptions flavor) {
#if ANDROID_SMP != 0
- int32_t encoding = 0xf3bf8f5f; // dmb in T1 encoding.
- Emit32(encoding);
+ int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding.
+ Emit32(encoding | flavor);
#endif
}
diff --git a/compiler/utils/arm/assembler_thumb2.h b/compiler/utils/arm/assembler_thumb2.h
index 9e7d394..b26173f 100644
--- a/compiler/utils/arm/assembler_thumb2.h
+++ b/compiler/utils/arm/assembler_thumb2.h
@@ -259,6 +259,9 @@ class Thumb2Assembler FINAL : public ArmAssembler {
void CompareAndBranchIfZero(Register r, Label* label) OVERRIDE;
void CompareAndBranchIfNonZero(Register r, Label* label) OVERRIDE;
+ // Memory barriers.
+ void dmb(DmbOptions flavor) OVERRIDE;
+
// Macros.
// Add signed constant value to rd. May clobber IP.
void AddConstant(Register rd, int32_t value, Condition cond = AL) OVERRIDE;
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index 092c891..702e03a 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -47,6 +47,16 @@ namespace arm {
#error "Exactly one of VFPv3_D16 or VFPv3_D32 can be defined at a time."
#endif
+// 4 bits option for the dmb instruction.
+// Order and values follows those of the ARM Architecture Reference Manual.
+enum DmbOptions {
+ SY = 0xf,
+ ST = 0xe,
+ ISH = 0xb,
+ ISHST = 0xa,
+ NSH = 0x7,
+ NSHST = 0x6
+};
enum ScaleFactor {
TIMES_1 = 0,