summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/dex/bb_optimizations.cc2
-rw-r--r--compiler/dex/compiler_ir.h3
-rw-r--r--compiler/dex/frontend.cc60
-rw-r--r--compiler/dex/mir_dataflow.cc4
-rw-r--r--compiler/dex/mir_graph.cc8
-rw-r--r--compiler/dex/mir_graph.h86
-rw-r--r--compiler/dex/mir_optimization.cc86
-rw-r--r--compiler/dex/portable/mir_to_gbc.cc2
-rw-r--r--compiler/dex/quick/codegen_util.cc28
-rw-r--r--compiler/dex/quick/mir_to_lir.h13
-rw-r--r--compiler/dex/quick/ralloc_util.cc32
-rw-r--r--compiler/dex/vreg_analysis.cc15
-rw-r--r--runtime/stack.h87
13 files changed, 339 insertions, 87 deletions
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index b6716e6..2ab6252 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -108,8 +108,6 @@ bool BBCombine::WalkBasicBlocks(CompilationUnit* cUnit, BasicBlock* bb) const {
* BasicBlock Optimization pass implementation start.
*/
void BBOptimizations::Start(CompilationUnit* cUnit) const {
- DCHECK_EQ(cUnit->num_compiler_temps, 0);
-
/*
* This pass has a different ordering depEnding on the suppress exception,
* so do the pass here for now:
diff --git a/compiler/dex/compiler_ir.h b/compiler/dex/compiler_ir.h
index 3798b45..32fd79b 100644
--- a/compiler/dex/compiler_ir.h
+++ b/compiler/dex/compiler_ir.h
@@ -81,9 +81,6 @@ struct CompilationUnit {
uint16_t num_outs;
uint16_t num_regs; // Unlike num_dalvik_registers, does not include ins.
- // TODO: may want to move this to MIRGraph.
- uint16_t num_compiler_temps;
-
// If non-empty, apply optimizer/debug flags only to matching methods.
std::string compiler_method_match;
// Flips sense of compiler_method_match - apply flags if doesn't match.
diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc
index f5bb85a..c2016d0 100644
--- a/compiler/dex/frontend.cc
+++ b/compiler/dex/frontend.cc
@@ -130,7 +130,6 @@ CompilationUnit::CompilationUnit(ArenaPool* pool)
num_ins(0),
num_outs(0),
num_regs(0),
- num_compiler_temps(0),
compiler_flip_match(false),
arena(pool),
mir_graph(NULL),
@@ -236,6 +235,43 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
cu.StartTimingSplit("BuildMIRGraph");
cu.mir_graph.reset(new MIRGraph(&cu, &cu.arena));
+ /*
+ * After creation of the MIR graph, also create the code generator.
+ * The reason we do this is that optimizations on the MIR graph may need to get information
+ * that is only available if a CG exists.
+ */
+#if defined(ART_USE_PORTABLE_COMPILER)
+ if (compiler_backend == kPortable) {
+ cu.cg.reset(PortableCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena, llvm_compilation_unit));
+ } else {
+#endif
+ Mir2Lir* mir_to_lir = nullptr;
+ switch (compiler.GetInstructionSet()) {
+ case kThumb2:
+ mir_to_lir = ArmCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena);
+ break;
+ case kMips:
+ mir_to_lir = MipsCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena);
+ break;
+ case kX86:
+ mir_to_lir = X86CodeGenerator(&cu, cu.mir_graph.get(), &cu.arena);
+ break;
+ default:
+ LOG(FATAL) << "Unexpected instruction set: " << compiler.GetInstructionSet();
+ }
+
+ cu.cg.reset(mir_to_lir);
+
+ /* The number of compiler temporaries depends on backend so set it up now if possible */
+ if (mir_to_lir) {
+ size_t max_temps = mir_to_lir->GetMaxPossibleCompilerTemps();
+ bool set_max = cu.mir_graph->SetMaxAvailableNonSpecialCompilerTemps(max_temps);
+ CHECK(set_max);
+ }
+#if defined(ART_USE_PORTABLE_COMPILER)
+ }
+#endif
+
/* Gathering opcode stats? */
if (kCompilerDebugFlags & (1 << kDebugCountOpcodes)) {
cu.mir_graph->EnableOpcodeCounting();
@@ -269,28 +305,6 @@ static CompiledMethod* CompileMethod(CompilerDriver& compiler,
CompiledMethod* result = NULL;
-#if defined(ART_USE_PORTABLE_COMPILER)
- if (compiler_backend == kPortable) {
- cu.cg.reset(PortableCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena, llvm_compilation_unit));
- } else {
-#endif
- switch (compiler.GetInstructionSet()) {
- case kThumb2:
- cu.cg.reset(ArmCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
- break;
- case kMips:
- cu.cg.reset(MipsCodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
- break;
- case kX86:
- cu.cg.reset(X86CodeGenerator(&cu, cu.mir_graph.get(), &cu.arena));
- break;
- default:
- LOG(FATAL) << "Unexpected instruction set: " << compiler.GetInstructionSet();
- }
-#if defined(ART_USE_PORTABLE_COMPILER)
- }
-#endif
-
cu.cg->Materialize();
cu.NewTimingSplit("Dedupe"); /* deduping takes up the vast majority of time in GetCompiledMethod(). */
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index c235448..9680450 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -1158,8 +1158,8 @@ void MIRGraph::CompilerInitializeSSAConversion() {
ssa_last_defs_[i] = 0;
}
- /* Add ssa reg for Method* */
- method_sreg_ = AddNewSReg(SSA_METHOD_BASEREG);
+ // Create a compiler temporary for Method*. This is done after SSA initialization.
+ GetNewCompilerTemp(kCompilerTempSpecialMethodPtr, false);
/*
* Allocate the BasicBlockDataFlow structure for the entry and code blocks
diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc
index 9dbb341..10bcdb9 100644
--- a/compiler/dex/mir_graph.cc
+++ b/compiler/dex/mir_graph.cc
@@ -46,7 +46,6 @@ const char* MIRGraph::extended_mir_op_names_[kMirOpLast - kMirOpFirst] = {
MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
: reg_location_(NULL),
- compiler_temps_(arena, 6, kGrowableArrayMisc),
cu_(cu),
ssa_base_vregs_(NULL),
ssa_subscripts_(NULL),
@@ -82,8 +81,13 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena)
checkstats_(NULL),
arena_(arena),
backward_branches_(0),
- forward_branches_(0) {
+ forward_branches_(0),
+ compiler_temps_(arena, 6, kGrowableArrayMisc),
+ num_non_special_compiler_temps_(0),
+ max_available_non_special_compiler_temps_(0) {
try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */);
+ max_available_special_compiler_temps_ = std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg))
+ - std::abs(static_cast<int>(kVRegTempBaseReg));
}
MIRGraph::~MIRGraph() {
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 4666d1e..debcb5c 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -168,11 +168,6 @@ enum OatMethodAttributes {
#define INVALID_REG (0xFF)
#define INVALID_OFFSET (0xDEADF00FU)
-/* SSA encodings for special registers */
-#define SSA_METHOD_BASEREG (-2)
-/* First compiler temp basereg, grows smaller */
-#define SSA_CTEMP_BASEREG (SSA_METHOD_BASEREG - 1)
-
#define MIR_IGNORE_NULL_CHECK (1 << kMIRIgnoreNullCheck)
#define MIR_NULL_CHECK_ONLY (1 << kMIRNullCheckOnly)
#define MIR_IGNORE_RANGE_CHECK (1 << kMIRIgnoreRangeCheck)
@@ -195,7 +190,13 @@ static const BasicBlockId NullBasicBlockId = 0;
* name of compiler-introduced temporaries.
*/
struct CompilerTemp {
- int32_t s_reg;
+ int32_t v_reg; // Virtual register number for temporary.
+ int32_t s_reg_low; // SSA name for low Dalvik word.
+};
+
+enum CompilerTempType {
+ kCompilerTempVR, // A virtual register temporary.
+ kCompilerTempSpecialMethodPtr, // Temporary that keeps track of current method pointer.
};
// When debug option enabled, records effectiveness of null and range check elimination.
@@ -571,10 +572,76 @@ class MIRGraph {
return bad_loc;
}
- int GetMethodSReg() {
+ int GetMethodSReg() const {
return method_sreg_;
}
+ /**
+ * @brief Used to obtain the number of compiler temporaries being used.
+ * @return Returns the number of compiler temporaries.
+ */
+ size_t GetNumUsedCompilerTemps() const {
+ size_t total_num_temps = compiler_temps_.Size();
+ DCHECK_LE(num_non_special_compiler_temps_, total_num_temps);
+ return total_num_temps;
+ }
+
+ /**
+ * @brief Used to obtain the number of non-special compiler temporaries being used.
+ * @return Returns the number of non-special compiler temporaries.
+ */
+ size_t GetNumNonSpecialCompilerTemps() const {
+ return num_non_special_compiler_temps_;
+ }
+
+ /**
+ * @brief Used to set the total number of available non-special compiler temporaries.
+ * @details Can fail setting the new max if there are more temps being used than the new_max.
+ * @param new_max The new maximum number of non-special compiler temporaries.
+ * @return Returns true if the max was set and false if failed to set.
+ */
+ bool SetMaxAvailableNonSpecialCompilerTemps(size_t new_max) {
+ if (new_max < GetNumNonSpecialCompilerTemps()) {
+ return false;
+ } else {
+ max_available_non_special_compiler_temps_ = new_max;
+ return true;
+ }
+ }
+
+ /**
+ * @brief Provides the number of non-special compiler temps available.
+ * @details Even if this returns zero, special compiler temps are guaranteed to be available.
+ * @return Returns the number of available temps.
+ */
+ size_t GetNumAvailableNonSpecialCompilerTemps();
+
+ /**
+ * @brief Used to obtain an existing compiler temporary.
+ * @param index The index of the temporary which must be strictly less than the
+ * number of temporaries.
+ * @return Returns the temporary that was asked for.
+ */
+ CompilerTemp* GetCompilerTemp(size_t index) const {
+ return compiler_temps_.Get(index);
+ }
+
+ /**
+ * @brief Used to obtain the maximum number of compiler temporaries that can be requested.
+ * @return Returns the maximum number of compiler temporaries, whether used or not.
+ */
+ size_t GetMaxPossibleCompilerTemps() const {
+ return max_available_special_compiler_temps_ + max_available_non_special_compiler_temps_;
+ }
+
+ /**
+ * @brief Used to obtain a new unique compiler temporary.
+ * @param ct_type Type of compiler temporary requested.
+ * @param wide Whether we should allocate a wide temporary.
+ * @return Returns the newly created compiler temporary.
+ */
+ CompilerTemp* GetNewCompilerTemp(CompilerTempType ct_type, bool wide);
+
bool MethodIsLeaf() {
return attributes_ & METHOD_IS_LEAF;
}
@@ -727,7 +794,6 @@ class MIRGraph {
// TODO: make these private.
RegLocation* reg_location_; // Map SSA names to location.
- GrowableArray<CompilerTemp*> compiler_temps_;
SafeMap<unsigned int, unsigned int> block_id_map_; // Block collapse lookup cache.
static const uint64_t oat_data_flow_attributes_[kMirOpLast];
@@ -836,6 +902,10 @@ class MIRGraph {
ArenaAllocator* arena_;
int backward_branches_;
int forward_branches_;
+ GrowableArray<CompilerTemp*> compiler_temps_;
+ size_t num_non_special_compiler_temps_;
+ size_t max_available_non_special_compiler_temps_;
+ size_t max_available_special_compiler_temps_;
};
} // namespace art
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 0d53d4c..209ed3d 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -199,13 +199,94 @@ int MIRGraph::GetSSAUseCount(int s_reg) {
return raw_use_counts_.Get(s_reg);
}
+size_t MIRGraph::GetNumAvailableNonSpecialCompilerTemps() {
+ if (num_non_special_compiler_temps_ >= max_available_non_special_compiler_temps_) {
+ return 0;
+ } else {
+ return max_available_non_special_compiler_temps_ - num_non_special_compiler_temps_;
+ }
+}
+
+static const RegLocation temp_loc = {kLocCompilerTemp,
+ 0, 1 /*defined*/, 0, 0, 0, 0, 0, 1 /*home*/,
+ kVectorNotUsed, INVALID_REG, INVALID_REG, INVALID_SREG,
+ INVALID_SREG};
+
+CompilerTemp* MIRGraph::GetNewCompilerTemp(CompilerTempType ct_type, bool wide) {
+ // There is a limit to the number of non-special temps so check to make sure it wasn't exceeded.
+ if (ct_type == kCompilerTempVR) {
+ size_t available_temps = GetNumAvailableNonSpecialCompilerTemps();
+ if (available_temps <= 0 || (available_temps <= 1 && wide)) {
+ return 0;
+ }
+ }
+
+ CompilerTemp *compiler_temp = static_cast<CompilerTemp *>(arena_->Alloc(sizeof(CompilerTemp),
+ ArenaAllocator::kAllocRegAlloc));
+
+ // Create the type of temp requested. Special temps need special handling because
+ // they have a specific virtual register assignment.
+ if (ct_type == kCompilerTempSpecialMethodPtr) {
+ DCHECK_EQ(wide, false);
+ compiler_temp->v_reg = static_cast<int>(kVRegMethodPtrBaseReg);
+ compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+
+ // The MIR graph keeps track of the sreg for method pointer specially, so record that now.
+ method_sreg_ = compiler_temp->s_reg_low;
+ } else {
+ DCHECK_EQ(ct_type, kCompilerTempVR);
+
+ // The new non-special compiler temp must receive a unique v_reg with a negative value.
+ compiler_temp->v_reg = static_cast<int>(kVRegNonSpecialTempBaseReg) - num_non_special_compiler_temps_;
+ compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+ num_non_special_compiler_temps_++;
+
+ if (wide) {
+ // Ensure that the two registers are consecutive. Since the virtual registers used for temps grow in a
+ // negative fashion, we need the smaller to refer to the low part. Thus, we redefine the v_reg and s_reg_low.
+ compiler_temp->v_reg--;
+ int ssa_reg_high = compiler_temp->s_reg_low;
+ compiler_temp->s_reg_low = AddNewSReg(compiler_temp->v_reg);
+ int ssa_reg_low = compiler_temp->s_reg_low;
+
+ // If needed initialize the register location for the high part.
+ // The low part is handled later in this method on a common path.
+ if (reg_location_ != nullptr) {
+ reg_location_[ssa_reg_high] = temp_loc;
+ reg_location_[ssa_reg_high].high_word = 1;
+ reg_location_[ssa_reg_high].s_reg_low = ssa_reg_low;
+ reg_location_[ssa_reg_high].wide = true;
+
+ // A new SSA needs new use counts.
+ use_counts_.Insert(0);
+ raw_use_counts_.Insert(0);
+ }
+
+ num_non_special_compiler_temps_++;
+ }
+ }
+
+ // Have we already allocated the register locations?
+ if (reg_location_ != nullptr) {
+ int ssa_reg_low = compiler_temp->s_reg_low;
+ reg_location_[ssa_reg_low] = temp_loc;
+ reg_location_[ssa_reg_low].s_reg_low = ssa_reg_low;
+ reg_location_[ssa_reg_low].wide = wide;
+
+ // A new SSA needs new use counts.
+ use_counts_.Insert(0);
+ raw_use_counts_.Insert(0);
+ }
+
+ compiler_temps_.Insert(compiler_temp);
+ return compiler_temp;
+}
/* Do some MIR-level extended basic block optimizations */
bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
if (bb->block_type == kDead) {
return true;
}
- int num_temps = 0;
bool use_lvn = bb->use_lvn;
UniquePtr<LocalValueNumbering> local_valnum;
if (use_lvn) {
@@ -468,9 +549,6 @@ bool MIRGraph::BasicBlockOpt(BasicBlock* bb) {
bb = ((cu_->disable_opt & (1 << kSuppressExceptionEdges)) != 0) ? NextDominatedBlock(bb) : NULL;
}
- if (num_temps > cu_->num_compiler_temps) {
- cu_->num_compiler_temps = num_temps;
- }
return true;
}
diff --git a/compiler/dex/portable/mir_to_gbc.cc b/compiler/dex/portable/mir_to_gbc.cc
index 70b660b..e6cc2de 100644
--- a/compiler/dex/portable/mir_to_gbc.cc
+++ b/compiler/dex/portable/mir_to_gbc.cc
@@ -1522,7 +1522,7 @@ void MirConverter::SetMethodInfo() {
reg_info.push_back(irb_->getInt32(cu_->num_ins));
reg_info.push_back(irb_->getInt32(cu_->num_regs));
reg_info.push_back(irb_->getInt32(cu_->num_outs));
- reg_info.push_back(irb_->getInt32(cu_->num_compiler_temps));
+ reg_info.push_back(irb_->getInt32(mir_graph_->GetNumUsedCompilerTemps()));
reg_info.push_back(irb_->getInt32(mir_graph_->GetNumSSARegs()));
::llvm::MDNode* reg_info_node = ::llvm::MDNode::get(*context_, reg_info);
inst->setMetadata("RegInfo", reg_info_node);
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 7f19ea1..ec5fedd 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -252,7 +252,7 @@ void Mir2Lir::DumpLIRInsn(LIR* lir, unsigned char* base_addr) {
}
void Mir2Lir::DumpPromotionMap() {
- int num_regs = cu_->num_dalvik_registers + cu_->num_compiler_temps + 1;
+ int num_regs = cu_->num_dalvik_registers + mir_graph_->GetNumUsedCompilerTemps();
for (int i = 0; i < num_regs; i++) {
PromotionMap v_reg_map = promotion_map_[i];
std::string buf;
@@ -289,7 +289,7 @@ void Mir2Lir::CodegenDump() {
LOG(INFO) << "Outs : " << cu_->num_outs;
LOG(INFO) << "CoreSpills : " << num_core_spills_;
LOG(INFO) << "FPSpills : " << num_fp_spills_;
- LOG(INFO) << "CompilerTemps : " << cu_->num_compiler_temps;
+ LOG(INFO) << "CompilerTemps : " << mir_graph_->GetNumUsedCompilerTemps();
LOG(INFO) << "Frame size : " << frame_size_;
LOG(INFO) << "code size is " << total_size_ <<
" bytes, Dalvik size is " << insns_size * 2;
@@ -1004,7 +1004,7 @@ Mir2Lir::Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena
first_lir_insn_(NULL),
last_lir_insn_(NULL) {
promotion_map_ = static_cast<PromotionMap*>
- (arena_->Alloc((cu_->num_dalvik_registers + cu_->num_compiler_temps + 1) *
+ (arena_->Alloc((cu_->num_dalvik_registers + mir_graph_->GetNumUsedCompilerTemps()) *
sizeof(promotion_map_[0]), ArenaAllocator::kAllocRegAlloc));
// Reserve pointer id 0 for NULL.
size_t null_idx = WrapPointer(NULL);
@@ -1081,13 +1081,27 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() {
return result;
}
+size_t Mir2Lir::GetMaxPossibleCompilerTemps() const {
+ // Chose a reasonably small value in order to contain stack growth.
+ // Backends that are smarter about spill region can return larger values.
+ const size_t max_compiler_temps = 10;
+ return max_compiler_temps;
+}
+
+size_t Mir2Lir::GetNumBytesForCompilerTempSpillRegion() {
+ // By default assume that the Mir2Lir will need one slot for each temporary.
+ // If the backend can better determine temps that have non-overlapping ranges and
+ // temps that do not need spilled, it can actually provide a small region.
+ return (mir_graph_->GetNumUsedCompilerTemps() * sizeof(uint32_t));
+}
+
int Mir2Lir::ComputeFrameSize() {
/* Figure out the frame size */
static const uint32_t kAlignMask = kStackAlignment - 1;
- uint32_t size = (num_core_spills_ + num_fp_spills_ +
- 1 /* filler word */ + cu_->num_regs + cu_->num_outs +
- cu_->num_compiler_temps + 1 /* cur_method* */)
- * sizeof(uint32_t);
+ uint32_t size = ((num_core_spills_ + num_fp_spills_ +
+ 1 /* filler word */ + cu_->num_regs + cu_->num_outs)
+ * sizeof(uint32_t)) +
+ GetNumBytesForCompilerTempSpillRegion();
/* Align and set */
return (size + kAlignMask) & ~(kAlignMask);
}
diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h
index bcd0eb1..35d0831 100644
--- a/compiler/dex/quick/mir_to_lir.h
+++ b/compiler/dex/quick/mir_to_lir.h
@@ -310,6 +310,19 @@ class Mir2Lir : public Backend {
void InsertLIRBefore(LIR* current_lir, LIR* new_lir);
void InsertLIRAfter(LIR* current_lir, LIR* new_lir);
+ /**
+ * @brief Provides the maximum number of compiler temporaries that the backend can/wants
+ * to place in a frame.
+ * @return Returns the maximum number of compiler temporaries.
+ */
+ size_t GetMaxPossibleCompilerTemps() const;
+
+ /**
+ * @brief Provides the number of bytes needed in frame for spilling of compiler temporaries.
+ * @return Returns the size in bytes for space needed for compiler temporary spill region.
+ */
+ size_t GetNumBytesForCompilerTempSpillRegion();
+
int ComputeFrameSize();
virtual void Materialize();
virtual CompiledMethod* GetCompiledMethod();
diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc
index 32c22f2..6d69512 100644
--- a/compiler/dex/quick/ralloc_util.cc
+++ b/compiler/dex/quick/ralloc_util.cc
@@ -132,9 +132,15 @@ int Mir2Lir::SRegToPMap(int s_reg) {
DCHECK_LT(v_reg, cu_->num_dalvik_registers);
return v_reg;
} else {
- int pos = std::abs(v_reg) - std::abs(SSA_METHOD_BASEREG);
- DCHECK_LE(pos, cu_->num_compiler_temps);
- return cu_->num_dalvik_registers + pos;
+ /*
+ * It must be the case that the v_reg for temporary is less than or equal to the
+ * base reg for temps. For that reason, "position" must be zero or positive.
+ */
+ unsigned int position = std::abs(v_reg) - std::abs(static_cast<int>(kVRegTempBaseReg));
+
+ // The temporaries are placed after dalvik registers in the promotion map
+ DCHECK_LT(position, mir_graph_->GetNumUsedCompilerTemps());
+ return cu_->num_dalvik_registers + position;
}
}
@@ -897,9 +903,8 @@ void Mir2Lir::DumpCounts(const RefCounts* arr, int size, const char* msg) {
* optimization is disabled.
*/
void Mir2Lir::DoPromotion() {
- int reg_bias = cu_->num_compiler_temps + 1;
int dalvik_regs = cu_->num_dalvik_registers;
- int num_regs = dalvik_regs + reg_bias;
+ int num_regs = dalvik_regs + mir_graph_->GetNumUsedCompilerTemps();
const int promotion_threshold = 1;
// Allow target code to add any special registers
@@ -926,16 +931,13 @@ void Mir2Lir::DoPromotion() {
for (int i = 0; i < dalvik_regs; i++) {
core_regs[i].s_reg = FpRegs[i].s_reg = i;
}
- // Set ssa name for Method*
- core_regs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg();
- FpRegs[dalvik_regs].s_reg = mir_graph_->GetMethodSReg(); // For consistecy.
- FpRegs[dalvik_regs + num_regs].s_reg = mir_graph_->GetMethodSReg(); // for consistency.
- // Set ssa names for compiler_temps
- for (int i = 1; i <= cu_->num_compiler_temps; i++) {
- CompilerTemp* ct = mir_graph_->compiler_temps_.Get(i);
- core_regs[dalvik_regs + i].s_reg = ct->s_reg;
- FpRegs[dalvik_regs + i].s_reg = ct->s_reg;
- FpRegs[num_regs + dalvik_regs + i].s_reg = ct->s_reg;
+
+ // Set ssa names for compiler temporaries
+ for (unsigned int ct_idx = 0; ct_idx < mir_graph_->GetNumUsedCompilerTemps(); ct_idx++) {
+ CompilerTemp* ct = mir_graph_->GetCompilerTemp(ct_idx);
+ core_regs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
+ FpRegs[dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
+ FpRegs[num_regs + dalvik_regs + ct_idx].s_reg = ct->s_reg_low;
}
// Duplicate in upper half to represent possible fp double starting sregs.
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index f211e3f..f8dc223 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -410,7 +410,8 @@ static const RegLocation fresh_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0,
void MIRGraph::InitRegLocations() {
/* Allocate the location map */
- RegLocation* loc = static_cast<RegLocation*>(arena_->Alloc(GetNumSSARegs() * sizeof(*loc),
+ int max_regs = GetNumSSARegs() + GetMaxPossibleCompilerTemps();
+ RegLocation* loc = static_cast<RegLocation*>(arena_->Alloc(max_regs * sizeof(*loc),
ArenaAllocator::kAllocRegAlloc));
for (int i = 0; i < GetNumSSARegs(); i++) {
loc[i] = fresh_loc;
@@ -418,13 +419,11 @@ void MIRGraph::InitRegLocations() {
loc[i].is_const = is_constant_v_->IsBitSet(i);
}
- /* Patch up the locations for Method* and the compiler temps */
- loc[method_sreg_].location = kLocCompilerTemp;
- loc[method_sreg_].defined = true;
- for (int i = 0; i < cu_->num_compiler_temps; i++) {
- CompilerTemp* ct = compiler_temps_.Get(i);
- loc[ct->s_reg].location = kLocCompilerTemp;
- loc[ct->s_reg].defined = true;
+ /* Patch up the locations for the compiler temps */
+ GrowableArray<CompilerTemp*>::Iterator iter(&compiler_temps_);
+ for (CompilerTemp* ct = iter.Next(); ct != NULL; ct = iter.Next()) {
+ loc[ct->s_reg_low].location = kLocCompilerTemp;
+ loc[ct->s_reg_low].defined = true;
}
reg_location_ = loc;
diff --git a/runtime/stack.h b/runtime/stack.h
index 590f406..0692390 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -52,6 +52,50 @@ enum VRegKind {
kUndefined,
};
+/**
+ * @brief Represents the virtual register numbers that denote special meaning.
+ * @details This is used to make some virtual register numbers to have specific
+ * semantic meaning. This is done so that the compiler can treat all virtual
+ * registers the same way and only special case when needed. For example,
+ * calculating SSA does not care whether a virtual register is a normal one or
+ * a compiler temporary, so it can deal with them in a consistent manner. But,
+ * for example if backend cares about temporaries because it has custom spill
+ * location, then it can special case them only then.
+ */
+enum VRegBaseRegNum : int {
+ /**
+ * @brief Virtual registers originating from dex have number >= 0.
+ */
+ kVRegBaseReg = 0,
+
+ /**
+ * @brief Invalid virtual register number.
+ */
+ kVRegInvalid = -1,
+
+ /**
+ * @brief Used to denote the base register for compiler temporaries.
+ * @details Compiler temporaries are virtual registers not originating
+ * from dex but that are created by compiler. All virtual register numbers
+ * that are <= kVRegTempBaseReg are categorized as compiler temporaries.
+ */
+ kVRegTempBaseReg = -2,
+
+ /**
+ * @brief Base register of temporary that holds the method pointer.
+ * @details This is a special compiler temporary because it has a specific
+ * location on stack.
+ */
+ kVRegMethodPtrBaseReg = kVRegTempBaseReg,
+
+ /**
+ * @brief Base register of non-special compiler temporary.
+ * @details A non-special compiler temporary is one whose spill location
+ * is flexible.
+ */
+ kVRegNonSpecialTempBaseReg = -3,
+};
+
// ShadowFrame has 3 possible layouts:
// - portable - a unified array of VRegs and references. Precise references need GC maps.
// - interpreter - separate VRegs and reference arrays. References are in the reference array.
@@ -524,8 +568,15 @@ class StackVisitor {
/*
* Return sp-relative offset for a Dalvik virtual register, compiler
* spill or Method* in bytes using Method*.
- * Note that (reg >= 0) refers to a Dalvik register, (reg == -2)
- * denotes Method* and (reg <= -3) denotes a compiler temp.
+ * Note that (reg >= 0) refers to a Dalvik register, (reg == -1)
+ * denotes an invalid Dalvik register, (reg == -2) denotes Method*
+ * and (reg <= -3) denotes a compiler temporary. A compiler temporary
+ * can be thought of as a virtual register that does not exist in the
+ * dex but holds intermediate values to help optimizations and code
+ * generation. A special compiler temporary is one whose location
+ * in frame is well known while non-special ones do not have a requirement
+ * on location in frame as long as code generator itself knows how
+ * to access them.
*
* +------------------------+
* | IN[ins-1] | {Note: resides in caller's frame}
@@ -546,9 +597,9 @@ class StackVisitor {
* | V[1] | ... (reg == 1)
* | V[0] | ... (reg == 0) <---- "locals_start"
* +------------------------+
- * | Compiler temps | ... (reg == -2)
- * | | ... (reg == -3)
- * | | ... (reg == -4)
+ * | Compiler temp region | ... (reg <= -3)
+ * | |
+ * | |
* +------------------------+
* | stack alignment padding| {0 to (kStackAlignWords-1) of padding}
* +------------------------+
@@ -556,23 +607,35 @@ class StackVisitor {
* | OUT[outs-2] |
* | . |
* | OUT[0] |
- * | curMethod* | ... (reg == -1) <<== sp, 16-byte aligned
+ * | curMethod* | ... (reg == -2) <<== sp, 16-byte aligned
* +========================+
*/
static int GetVRegOffset(const DexFile::CodeItem* code_item,
uint32_t core_spills, uint32_t fp_spills,
size_t frame_size, int reg) {
DCHECK_EQ(frame_size & (kStackAlignment - 1), 0U);
+ DCHECK_NE(reg, static_cast<int>(kVRegInvalid));
+
int num_spills = __builtin_popcount(core_spills) + __builtin_popcount(fp_spills) + 1; // Filler.
int num_ins = code_item->ins_size_;
int num_regs = code_item->registers_size_ - num_ins;
int locals_start = frame_size - ((num_spills + num_regs) * sizeof(uint32_t));
- if (reg == -2) {
- return 0; // Method*
- } else if (reg <= -3) {
- return locals_start - ((reg + 1) * sizeof(uint32_t)); // Compiler temp.
- } else if (reg < num_regs) {
- return locals_start + (reg * sizeof(uint32_t)); // Dalvik local reg.
+ if (reg == static_cast<int>(kVRegMethodPtrBaseReg)) {
+ // The current method pointer corresponds to special location on stack.
+ return 0;
+ } else if (reg <= static_cast<int>(kVRegNonSpecialTempBaseReg)) {
+ /*
+ * Special temporaries may have custom locations and the logic above deals with that.
+ * However, non-special temporaries are placed relative to the locals. Since the
+ * virtual register numbers for temporaries "grow" in negative direction, reg number
+ * will always be <= to the temp base reg. Thus, the logic ensures that the first
+ * temp is at offset -4 bytes from locals, the second is at -8 bytes from locals,
+ * and so on.
+ */
+ int relative_offset = (reg + std::abs(static_cast<int>(kVRegNonSpecialTempBaseReg)) - 1) * sizeof(uint32_t);
+ return locals_start + relative_offset;
+ } else if (reg < num_regs) {
+ return locals_start + (reg * sizeof(uint32_t));
} else {
return frame_size + ((reg - num_regs) * sizeof(uint32_t)) + sizeof(uint32_t); // Dalvik in.
}