am 1e1cfa25: Merge "Restore build-art minimal build"
* commit '1e1cfa25d603ae882c6d362f4a90f546164dbdba':
Restore build-art minimal build
diff --git a/compiler/Android.mk b/compiler/Android.mk
index df77853..86f5213 100644
--- a/compiler/Android.mk
+++ b/compiler/Android.mk
@@ -96,7 +96,8 @@
sea_ir/frontend.cc \
sea_ir/instruction_tools.cc \
sea_ir/sea.cc \
- sea_ir/code_gen.cc
+ sea_ir/code_gen.cc \
+ sea_ir/types/type_inference.cc
endif
LIBART_COMPILER_CFLAGS :=
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 1ee29cb..60e638c 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -209,8 +209,8 @@
void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) {
DCHECK(inst->Opcode() == Instruction::RETURN_VOID);
- // Are we compiling a constructor ?
- if ((unit_.GetAccessFlags() & kAccConstructor) == 0) {
+ // Are we compiling a non-clinit constructor?
+ if (!unit_.IsConstructor() || unit_.IsStatic()) {
return;
}
// Do we need a constructor barrier ?
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index 5bf0086..465139b 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -80,6 +80,10 @@
return access_flags_;
}
+ bool IsConstructor() const {
+ return ((access_flags_ & kAccConstructor) != 0);
+ }
+
bool IsNative() const {
return ((access_flags_ & kAccNative) != 0);
}
diff --git a/compiler/sea_ir/code_gen.cc b/compiler/sea_ir/code_gen.cc
index a513907..0ef21b4 100644
--- a/compiler/sea_ir/code_gen.cc
+++ b/compiler/sea_ir/code_gen.cc
@@ -123,14 +123,14 @@
void CodeGenVisitor::Visit(ReturnInstructionNode* instruction) {
std::string instr = instruction->GetInstruction()->DumpString(NULL);
std::cout << "2.Instruction: " << instr << std::endl;
- DCHECK_GT(instruction->GetSSAUses().size(), 0u);
- llvm::Value* return_value = llvm_data_->GetValue(instruction->GetSSAUses().at(0));
+ DCHECK_GT(instruction->GetSSAProducers().size(), 0u);
+ llvm::Value* return_value = llvm_data_->GetValue(instruction->GetSSAProducers().at(0));
llvm_data_->builder_.CreateRet(return_value);
}
void CodeGenVisitor::Visit(IfNeInstructionNode* instruction) {
std::string instr = instruction->GetInstruction()->DumpString(NULL);
std::cout << "3.Instruction: " << instr << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
+ std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
DCHECK_GT(ssa_uses.size(), 1u);
InstructionNode* use_l = ssa_uses.at(0);
llvm::Value* left = llvm_data_->GetValue(use_l);
@@ -171,7 +171,7 @@
// since their purpose of minimizing the number of opcodes in dex is
// not relevant for the IR. (Will need to have different
// instruction subclasses for functions and procedures.)
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
+ std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
InstructionNode* use_l = ssa_uses.at(0);
llvm::Value* left = llvm_data_->GetValue(use_l);
llvm::Value* right = llvm::ConstantInt::get(*llvm_data_->context_, llvm::APInt(32, 0));
@@ -187,7 +187,7 @@
// TODO: Add proper checking of the matching between formal and actual signature.
DCHECK(NULL != callee);
std::vector<llvm::Value*> parameter_values;
- std::vector<InstructionNode*> parameter_sources = invoke->GetSSAUses();
+ std::vector<InstructionNode*> parameter_sources = invoke->GetSSAProducers();
for (std::vector<InstructionNode*>::const_iterator cit = parameter_sources.begin();
cit != parameter_sources.end(); ++cit) {
llvm::Value* parameter_value = llvm_data_->GetValue((*cit));
@@ -201,7 +201,7 @@
void CodeGenVisitor::Visit(AddIntInstructionNode* instruction) {
std::string instr = instruction->GetInstruction()->DumpString(NULL);
std::cout << "7.Instruction: " << instr << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
+ std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
DCHECK_GT(ssa_uses.size(), 1u);
InstructionNode* use_l = ssa_uses.at(0);
InstructionNode* use_r = ssa_uses.at(1);
@@ -221,7 +221,7 @@
void CodeGenVisitor::Visit(IfEqzInstructionNode* instruction) {
std::string instr = instruction->GetInstruction()->DumpString(NULL);
std::cout << "9. Instruction: " << instr << "; Id: " <<instruction << std::endl;
- std::vector<InstructionNode*> ssa_uses = instruction->GetSSAUses();
+ std::vector<InstructionNode*> ssa_uses = instruction->GetSSAProducers();
DCHECK_GT(ssa_uses.size(), 0u);
InstructionNode* use_l = ssa_uses.at(0);
llvm::Value* left = llvm_data_->GetValue(use_l);
diff --git a/compiler/sea_ir/code_gen.h b/compiler/sea_ir/code_gen.h
index aba8d5c..f656453 100644
--- a/compiler/sea_ir/code_gen.h
+++ b/compiler/sea_ir/code_gen.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_SEA_IR_CODE_GEN_H_
#define ART_COMPILER_SEA_IR_CODE_GEN_H_
+#include "llvm/Analysis/Verifier.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
diff --git a/compiler/sea_ir/frontend.cc b/compiler/sea_ir/frontend.cc
index 5843388..cc49ea5 100644
--- a/compiler/sea_ir/frontend.cc
+++ b/compiler/sea_ir/frontend.cc
@@ -30,7 +30,7 @@
static CompiledMethod* CompileMethodWithSeaIr(CompilerDriver& compiler,
const CompilerBackend compiler_backend,
const DexFile::CodeItem* code_item,
- uint32_t access_flags, InvokeType invoke_type,
+ uint32_t method_access_flags, InvokeType invoke_type,
uint32_t class_def_idx, uint32_t method_idx,
jobject class_loader, const DexFile& dex_file
#if defined(ART_USE_PORTABLE_COMPILER)
@@ -41,7 +41,7 @@
// and silencing the cpplint.py warning, I just corrected the formatting.
VLOG(compiler) << "Compiling " << PrettyMethod(method_idx, dex_file) << "...";
sea_ir::SeaGraph* sg = sea_ir::SeaGraph::GetCurrentGraph(dex_file);
- sg->CompileMethod(code_item, class_def_idx, method_idx, dex_file);
+ sg->CompileMethod(code_item, class_def_idx, method_idx, method_access_flags, dex_file);
sg->DumpSea("/tmp/temp.dot");
CHECK(0 && "No SEA compiled function exists yet.");
return NULL;
@@ -50,14 +50,14 @@
CompiledMethod* SeaIrCompileOneMethod(CompilerDriver& compiler,
const CompilerBackend backend,
const DexFile::CodeItem* code_item,
- uint32_t access_flags,
+ uint32_t method_access_flags,
InvokeType invoke_type,
uint32_t class_def_idx,
uint32_t method_idx,
jobject class_loader,
const DexFile& dex_file,
llvm::LlvmCompilationUnit* llvm_compilation_unit) {
- return CompileMethodWithSeaIr(compiler, backend, code_item, access_flags, invoke_type,
+ return CompileMethodWithSeaIr(compiler, backend, code_item, method_access_flags, invoke_type,
class_def_idx, method_idx, class_loader, dex_file
#if defined(ART_USE_PORTABLE_COMPILER)
, llvm_compilation_unit
@@ -68,13 +68,13 @@
extern "C" art::CompiledMethod*
SeaIrCompileMethod(art::CompilerDriver& compiler,
const art::DexFile::CodeItem* code_item,
- uint32_t access_flags, art::InvokeType invoke_type,
+ uint32_t method_access_flags, art::InvokeType invoke_type,
uint32_t class_def_idx, uint32_t method_idx, jobject class_loader,
const art::DexFile& dex_file) {
// TODO: Check method fingerprint here to determine appropriate backend type.
// Until then, use build default
art::CompilerBackend backend = compiler.GetCompilerBackend();
- return art::SeaIrCompileOneMethod(compiler, backend, code_item, access_flags, invoke_type,
+ return art::SeaIrCompileOneMethod(compiler, backend, code_item, method_access_flags, invoke_type,
class_def_idx, method_idx, class_loader, dex_file,
NULL /* use thread llvm_info */);
}
diff --git a/compiler/sea_ir/instruction_nodes.h b/compiler/sea_ir/instruction_nodes.h
index 6f9bddd..1b81e9a 100644
--- a/compiler/sea_ir/instruction_nodes.h
+++ b/compiler/sea_ir/instruction_nodes.h
@@ -61,7 +61,7 @@
}
// Returns the ordered set of Instructions that define the input operands of this instruction.
// Precondition: SeaGraph.ConvertToSSA().
- std::vector<InstructionNode*> GetSSAUses() {
+ std::vector<InstructionNode*> GetSSAProducers() {
std::vector<int> uses = GetUses();
std::vector<InstructionNode*> ssa_uses;
for (std::vector<int>::const_iterator cit = uses.begin(); cit != uses.end(); cit++) {
@@ -70,6 +70,10 @@
return ssa_uses;
}
+ std::vector<InstructionNode*> GetSSAConsumers() {
+ return used_in_;
+ }
+
virtual void AddSSAUse(InstructionNode* use) {
used_in_.push_back(use);
}
diff --git a/compiler/sea_ir/sea.cc b/compiler/sea_ir/sea.cc
index 99b21f8..585b2aa 100644
--- a/compiler/sea_ir/sea.cc
+++ b/compiler/sea_ir/sea.cc
@@ -18,6 +18,7 @@
#include "instruction_tools.h"
#include "sea.h"
#include "code_gen.h"
+#include "types/type_inference.h"
#define MAX_REACHING_DEF_ITERERATIONS (10)
// TODO: When development is done, this define should not
@@ -191,10 +192,12 @@
void SeaGraph::BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
- const art::DexFile& dex_file, uint32_t class_def_idx, uint32_t method_idx) {
+ const art::DexFile& dex_file, uint32_t class_def_idx,
+ uint32_t method_idx, uint32_t method_access_flags) {
+ code_item_ = code_item;
class_def_idx_ = class_def_idx;
method_idx_ = method_idx;
-
+ method_access_flags_ = method_access_flags;
const uint16_t* code = code_item->insns_;
const size_t size_in_code_units = code_item->insns_size_in_code_units_;
// This maps target instruction pointers to their corresponding region objects.
@@ -225,8 +228,9 @@
// Insert one SignatureNode per function argument,
// to serve as placeholder definitions in dataflow analysis.
for (unsigned int crt_offset = 0; crt_offset < code_item->ins_size_; crt_offset++) {
+ int position = crt_offset; // TODO: Is this the correct offset in the signature?
SignatureNode* parameter_def_node =
- new sea_ir::SignatureNode(code_item->registers_size_ - 1 - crt_offset);
+ new sea_ir::SignatureNode(code_item->registers_size_ - 1 - crt_offset, position);
AddParameterNode(parameter_def_node);
r->AddChild(parameter_def_node);
}
@@ -260,12 +264,7 @@
}
r = nextRegion;
}
- bool definesRegister = (0 != InstructionTools::instruction_attributes_[inst->Opcode()]
- && (1 << kDA));
- LOG(INFO)<< inst->GetDexPc(code) << "*** " << inst->DumpString(&dex_file)
- << " region:" <<r->StringId() << "Definition?" << definesRegister << std::endl;
- r->AddChild(node);
- }
+ }
i += inst->SizeInCodeUnits();
}
}
@@ -417,10 +416,10 @@
code_gen_postpass_visitor.Write(std::string("my_file.llvm"));
}
-void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item,
- uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file) {
+void SeaGraph::CompileMethod(const art::DexFile::CodeItem* code_item, uint32_t class_def_idx,
+ uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file) {
// Two passes: Builds the intermediate structure (non-SSA) of the sea-ir for the function.
- BuildMethodSeaGraph(code_item, dex_file, class_def_idx, method_idx);
+ BuildMethodSeaGraph(code_item, dex_file, class_def_idx, method_idx, method_access_flags);
// Pass: Compute reverse post-order of regions.
ComputeRPO();
// Multiple passes: compute immediate dominators.
@@ -433,6 +432,9 @@
ComputeDominanceFrontier();
// Two Passes: Phi node insertion.
ConvertToSSA();
+ // Pass: type inference
+ TypeInference ti = TypeInference();
+ ti.ComputeTypes(this);
// Pass: Generate LLVM IR.
GenerateLLVM();
}
diff --git a/compiler/sea_ir/sea.h b/compiler/sea_ir/sea.h
index 5cb8424..efdbb3b 100644
--- a/compiler/sea_ir/sea.h
+++ b/compiler/sea_ir/sea.h
@@ -57,8 +57,10 @@
// can return from the GetSSAUses() calls, instead of having missing SSA edges.
class SignatureNode: public InstructionNode {
public:
- explicit SignatureNode(unsigned int parameter_register):InstructionNode(NULL),
- parameter_register_(parameter_register) { }
+ // Creates a new signature node representing the initial definition of the
+ // register @parameter_register which is the @position-th argument to the method.
+ explicit SignatureNode(unsigned int parameter_register, unsigned int position):
+ InstructionNode(NULL), parameter_register_(parameter_register), position_(position) { }
void ToDot(std::string& result, const art::DexFile& dex_file) const {
result += StringId() +" [label=\"signature:";
@@ -71,6 +73,10 @@
return parameter_register_;
}
+ unsigned int GetPositionInSignature() {
+ return position_;
+ }
+
std::vector<int> GetUses() {
return std::vector<int>();
}
@@ -82,6 +88,7 @@
private:
unsigned int parameter_register_;
+ unsigned int position_; // The position of this parameter node is in the function parameter list.
};
class PhiInstructionNode: public InstructionNode {
@@ -259,8 +266,8 @@
public:
static SeaGraph* GetCurrentGraph(const art::DexFile&);
- void CompileMethod(const art::DexFile::CodeItem* code_item,
- uint32_t class_def_idx, uint32_t method_idx, const art::DexFile& dex_file);
+ void CompileMethod(const art::DexFile::CodeItem* code_item, uint32_t class_def_idx,
+ uint32_t method_idx, uint32_t method_access_flags, const art::DexFile& dex_file);
// Returns all regions corresponding to this SeaGraph.
std::vector<Region*>* GetRegions() {
return ®ions_;
@@ -275,12 +282,19 @@
std::vector<SignatureNode*>* GetParameterNodes() {
return ¶meters_;
}
+
+ const art::DexFile* GetDexFile() const {
+ return &dex_file_;
+ }
+
uint32_t class_def_idx_;
uint32_t method_idx_;
+ uint32_t method_access_flags_;
private:
explicit SeaGraph(const art::DexFile& df):
- class_def_idx_(0), method_idx_(0), regions_(), parameters_(), dex_file_(df) {
+ class_def_idx_(0), method_idx_(0), method_access_flags_(), regions_(),
+ parameters_(), dex_file_(df), code_item_(NULL) {
}
// Registers @childReg as a region belonging to the SeaGraph instance.
void AddRegion(Region* childReg);
@@ -295,7 +309,8 @@
// Builds the non-SSA sea-ir representation of the function @code_item from @dex_file
// with class id @class_def_idx and method id @method_idx.
void BuildMethodSeaGraph(const art::DexFile::CodeItem* code_item,
- const art::DexFile& dex_file, uint32_t class_def_idx, uint32_t method_idx);
+ const art::DexFile& dex_file, uint32_t class_def_idx,
+ uint32_t method_idx, uint32_t method_access_flags);
// Computes immediate dominators for each region.
// Precondition: ComputeMethodSeaGraph()
void ComputeIDominators();
@@ -336,6 +351,7 @@
std::vector<Region*> regions_;
std::vector<SignatureNode*> parameters_;
const art::DexFile& dex_file_;
+ const art::DexFile::CodeItem* code_item_;
};
} // namespace sea_ir
#endif // ART_COMPILER_SEA_IR_SEA_H_
diff --git a/compiler/sea_ir/types/type_inference.cc b/compiler/sea_ir/types/type_inference.cc
new file mode 100644
index 0000000..ad81310
--- /dev/null
+++ b/compiler/sea_ir/types/type_inference.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sea_ir/types/type_inference.h"
+
+namespace sea_ir {
+
+bool TypeInference::IsPrimitiveDescriptor(char descriptor) {
+ switch (descriptor) {
+ case 'I':
+ case 'C':
+ case 'S':
+ case 'B':
+ case 'Z':
+ case 'F':
+ case 'D':
+ case 'J':
+ return true;
+ default:
+ return false;
+ }
+}
+
+FunctionTypeInfo::FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types)
+ : dex_file_(graph->GetDexFile()), dex_method_idx_(graph->method_idx_), type_cache_(types),
+ method_access_flags_(graph->method_access_flags_) {
+ const art::DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
+ const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
+ declaring_class_ = &(type_cache_->FromDescriptor(NULL, descriptor, false));
+}
+
+std::vector<const Type*> FunctionTypeInfo::GetDeclaredArgumentTypes()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::vector<const Type*> argument_types;
+ // Include the "this" pointer.
+ size_t cur_arg = 0;
+ if (!IsStatic()) {
+ // If this is a constructor for a class other than java.lang.Object, mark the first ("this")
+ // argument as uninitialized. This restricts field access until the superclass constructor is
+ // called.
+ const art::verifier::RegType& declaring_class = GetDeclaringClass();
+ if (IsConstructor() && !declaring_class.IsJavaLangObject()) {
+ argument_types.push_back(&(type_cache_->UninitializedThisArgument(declaring_class)));
+ } else {
+ argument_types.push_back(&declaring_class);
+ }
+ cur_arg++;
+ }
+
+ const art::DexFile::ProtoId& proto_id =
+ dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
+ art::DexFileParameterIterator iterator(*dex_file_, proto_id);
+
+ for (; iterator.HasNext(); iterator.Next()) {
+ const char* descriptor = iterator.GetDescriptor();
+ if (descriptor == NULL) {
+ LOG(FATAL) << "Error: Encountered null type descriptor for function argument.";
+ }
+ switch (descriptor[0]) {
+ case 'L':
+ case '[':
+ // We assume that reference arguments are initialized. The only way it could be otherwise
+ // (assuming the caller was verified) is if the current method is <init>, but in that case
+ // it's effectively considered initialized the instant we reach here (in the sense that we
+ // can return without doing anything or call virtual methods).
+ {
+ const Type& reg_type = type_cache_->FromDescriptor(NULL, descriptor, false);
+ argument_types.push_back(®_type);
+ }
+ break;
+ case 'Z':
+ argument_types.push_back(&type_cache_->Boolean());
+ break;
+ case 'C':
+ argument_types.push_back(&type_cache_->Char());
+ break;
+ case 'B':
+ argument_types.push_back(&type_cache_->Byte());
+ break;
+ case 'I':
+ argument_types.push_back(&type_cache_->Integer());
+ break;
+ case 'S':
+ argument_types.push_back(&type_cache_->Short());
+ break;
+ case 'F':
+ argument_types.push_back(&type_cache_->Float());
+ break;
+ case 'J':
+ case 'D': {
+ // TODO: Figure out strategy for two-register operands (double, long)
+ LOG(FATAL) << "Error: Type inference for 64-bit variables has not been implemented.";
+ break;
+ }
+ default:
+ LOG(FATAL) << "Error: Unexpected signature encountered during type inference.";
+ }
+ cur_arg++;
+ }
+ return argument_types;
+}
+
+// TODO: Lock is only used for dumping types (during development). Remove this for performance.
+void TypeInference::ComputeTypes(SeaGraph* graph) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::vector<Region*>* regions = graph->GetRegions();
+ std::list<InstructionNode*> worklist;
+ // Fill the work-list with all instructions.
+ for (std::vector<Region*>::const_iterator region_it = regions->begin();
+ region_it != regions->end(); region_it++) {
+ std::vector<PhiInstructionNode*>* phi_instructions = (*region_it)->GetPhiNodes();
+ std::copy(phi_instructions->begin(), phi_instructions->end(), std::back_inserter(worklist));
+ std::vector<InstructionNode*>* instructions = (*region_it)->GetInstructions();
+ std::copy(instructions->begin(), instructions->end(), std::back_inserter(worklist));
+ }
+ TypeInferenceVisitor tiv(graph, type_cache_);
+ // Sparse (SSA) fixed-point algorithm that processes each instruction in the work-list,
+ // adding consumers of instructions whose result changed type back into the work-list.
+ // Note: According to [1] list iterators should not be invalidated on insertion,
+ // which simplifies the implementation; not 100% sure other STL implementations
+ // maintain this invariant, but they should.
+ // [1] http://www.sgi.com/tech/stl/List.html
+ // TODO: Making this conditional (as in sparse conditional constant propagation) would be good.
+ for (std::list<InstructionNode*>::const_iterator instruction_it = worklist.begin();
+ instruction_it != worklist.end(); instruction_it++) {
+ std::cout << "Instruction: " << (*instruction_it)->Id() << std::endl;
+ (*instruction_it)->Accept(&tiv);
+ std::map<int, const Type*>::const_iterator old_result_it =
+ type_map_.find((*instruction_it)->Id());
+ const Type* new_type = tiv.GetType();
+ bool first_time_set = (old_result_it == type_map_.end()) && (new_type != NULL);
+ bool type_changed = (old_result_it != type_map_.end()) && ((*old_result_it).second != new_type);
+ if (first_time_set || type_changed) {
+ std::cout << " New type:" << new_type->IsIntegralTypes() << std::endl;
+ std::cout << " Descriptor:" << new_type->Dump() << std::endl;
+ type_map_[(*instruction_it)->Id()] = new_type;
+ // Add SSA consumers of the current instruction to the work-list.
+ std::vector<InstructionNode*> consumers = (*instruction_it)->GetSSAConsumers();
+ for (std::vector<InstructionNode*>::iterator consumer = consumers.begin();
+ consumer != consumers.end(); consumer++) {
+ worklist.push_back(*consumer);
+ }
+ }
+ }
+}
+
+} // namespace sea_ir
diff --git a/compiler/sea_ir/types/type_inference.h b/compiler/sea_ir/types/type_inference.h
new file mode 100644
index 0000000..1c0d42e
--- /dev/null
+++ b/compiler/sea_ir/types/type_inference.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
+#define ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
+
+#include "sea_ir/sea.h"
+#include "dex_file-inl.h"
+#include "verifier/reg_type.h"
+#include "verifier/reg_type_cache.h"
+#include "verifier/reg_type_cache.h"
+namespace sea_ir {
+
+typedef art::verifier::RegType Type;
+
+
+// The type inference in SEA IR is different from the verifier in that it is concerned
+// with a rich type hierarchy (TODO) usable in optimization and does not perform
+// precise verification which is the job of the verifier.
+class TypeInference {
+ public:
+ TypeInference() {
+ type_cache_ = new art::verifier::RegTypeCache(false);
+ }
+
+ // Computes the types for the method with SEA IR representation provided by @graph.
+ void ComputeTypes(SeaGraph* graph);
+
+ // Returns true if @descriptor corresponds to a primitive type.
+ static bool IsPrimitiveDescriptor(char descriptor);
+
+ protected:
+ art::verifier::RegTypeCache* type_cache_;
+ std::map<int, const Type*> type_map_;
+};
+
+// Stores information about the exact type of a function.
+class FunctionTypeInfo {
+ public:
+ // @graph provides the input method SEA IR representation.
+ // @types provides the input cache of types from which the
+ // parameter types of the function are found.
+ FunctionTypeInfo(const SeaGraph* graph, art::verifier::RegTypeCache* types);
+ // Returns the ordered vector of types corresponding to the function arguments.
+ std::vector<const Type*> GetDeclaredArgumentTypes();
+ // Returns the type corresponding to the class that declared the method.
+ const Type& GetDeclaringClass() {
+ return *declaring_class_;
+ }
+
+ bool IsConstructor() const {
+ return (method_access_flags_ & kAccConstructor) != 0;
+ }
+
+ bool IsStatic() const {
+ return (method_access_flags_ & kAccStatic) != 0;
+ }
+
+ protected:
+ const Type* declaring_class_;
+ const art::DexFile* dex_file_;
+ const uint32_t dex_method_idx_;
+ art::verifier::RegTypeCache* type_cache_;
+ const uint32_t method_access_flags_; // Method's access flags.
+};
+
+// The TypeInferenceVisitor visits each instruction and computes its type taking into account
+// the current type of the operands. The type is stored in the visitor.
+// We may be better off by using a separate visitor type hierarchy that has return values
+// or that passes data as parameters, than to use fields to store information that should
+// in fact be returned after visiting each element. Ideally, I would prefer to use templates
+// to specify the returned value type, but I am not aware of a possible implementation
+// that does not horribly duplicate the visitor infrastructure code (version 1: no return value,
+// version 2: with template return value).
+class TypeInferenceVisitor: public IRVisitor {
+ public:
+ TypeInferenceVisitor(SeaGraph* graph, art::verifier::RegTypeCache* types):
+ graph_(graph), type_cache_(types), crt_type_() { }
+ void Initialize(SeaGraph* graph) { }
+ // There are no type related actions to be performed on these classes.
+ void Visit(SeaGraph* graph) { }
+ void Visit(Region* region) { }
+
+ void Visit(PhiInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(SignatureNode* parameter) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ std::cout << "[TI] Visiting signature node:" << parameter->GetResultRegister() << std::endl;
+ FunctionTypeInfo fti(graph_, type_cache_);
+ std::vector<const Type*> arguments = fti.GetDeclaredArgumentTypes();
+ crt_type_.clear();
+ std::cout << "Pos:" << parameter->GetPositionInSignature() << "/" << arguments.size() <<std::endl;
+ DCHECK_LT(parameter->GetPositionInSignature(), arguments.size())
+ << "Signature node position not present in signature.";
+ crt_type_.push_back(arguments.at(parameter->GetPositionInSignature()));
+ }
+ void Visit(InstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(ConstInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(ReturnInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(IfNeInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(MoveResultInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(InvokeStaticInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(AddIntInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(GotoInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+ void Visit(IfEqzInstructionNode* instruction) {
+ std::cout << "[TI] Visiting node:" << instruction->Id() << std::endl;
+ }
+
+ const Type* GetType() const {
+ // TODO: Currently multiple defined types are not supported.
+ if (crt_type_.size()>0) return crt_type_.at(0);
+ return NULL;
+ }
+
+ protected:
+ const SeaGraph* graph_;
+ art::verifier::RegTypeCache* type_cache_;
+ std::vector<const Type*> crt_type_; // Stored temporarily between two calls to Visit.
+};
+
+} // namespace sea_ir
+
+#endif // ART_COMPILER_SEA_IR_TYPES_TYPE_INFERENCE_H_
diff --git a/compiler/sea_ir/visitor.h b/compiler/sea_ir/visitor.h
index a4fec7b..f4cecd8 100644
--- a/compiler/sea_ir/visitor.h
+++ b/compiler/sea_ir/visitor.h
@@ -17,14 +17,6 @@
#ifndef ART_COMPILER_SEA_IR_VISITOR_H_
#define ART_COMPILER_SEA_IR_VISITOR_H_
-#include "llvm/IR/IRBuilder.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Analysis/Verifier.h"
-// TODO: Separating the root visitor from the code_gen visitor
-// would allow me to not include llvm headers here.
-
-
namespace sea_ir {
class SeaGraph;
@@ -66,7 +58,7 @@
virtual void Visit(GotoInstructionNode* instruction) = 0;
virtual void Visit(IfEqzInstructionNode* instruction) = 0;
- // Note: This favor of visitor separates the traversal functions from the actual visiting part
+ // Note: This flavor of visitor separates the traversal functions from the actual visiting part
// so that the Visitor subclasses don't duplicate code and can't get the traversal wrong.
// The disadvantage is the increased number of functions (and calls).
virtual void Traverse(SeaGraph* graph);
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index cbdc430..aaff0fc 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -313,7 +313,6 @@
method_ids_ = reinterpret_cast<const MethodId*>(b + h->method_ids_off_);
proto_ids_ = reinterpret_cast<const ProtoId*>(b + h->proto_ids_off_);
class_defs_ = reinterpret_cast<const ClassDef*>(b + h->class_defs_off_);
- DCHECK_EQ(size_, header_->file_size_) << GetLocation();
}
bool DexFile::CheckMagicAndVersion() const {
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index 97126cb..c64caa8 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -101,7 +101,8 @@
uint16_t String::CharAt(int32_t index) const {
// TODO: do we need this? Equals is the only caller, and could
// bounds check itself.
- if (index < 0 || index >= count_) {
+ DCHECK_GE(count_, 0); // ensures the unsigned comparison is safe.
+ if (UNLIKELY(static_cast<uint32_t>(index) >= static_cast<uint32_t>(count_))) {
Thread* self = Thread::Current();
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionF(throw_location, "Ljava/lang/StringIndexOutOfBoundsException;",
diff --git a/runtime/stack.cc b/runtime/stack.cc
index aeb15f0..a74bcdb 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -266,7 +266,11 @@
// Frame sanity.
size_t frame_size = method->GetFrameSizeInBytes();
CHECK_NE(frame_size, 0u);
- CHECK_LT(frame_size, 1024u);
+ // A rough guess at an upper size we expect to see for a frame. The 256 is
+ // a dex register limit. The 16 incorporates callee save spills and
+ // outgoing argument set up.
+ const size_t kMaxExpectedFrameSize = 256 * sizeof(word) + 16;
+ CHECK_LE(frame_size, kMaxExpectedFrameSize);
size_t return_pc_offset = method->GetReturnPcOffsetInBytes();
CHECK_LT(return_pc_offset, frame_size);
}
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 9f0d911..95fda9d 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2507,8 +2507,8 @@
// Special instructions.
case Instruction::RETURN_VOID_BARRIER:
- DCHECK(Runtime::Current()->IsStarted());
- if (!IsConstructor()) {
+ DCHECK(Runtime::Current()->IsStarted()) << PrettyMethod(dex_method_idx_, *dex_file_);
+ if (!IsConstructor() || IsStatic()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "return-void-barrier not expected";
}
break;
@@ -2819,8 +2819,10 @@
dex_cache_->SetResolvedType(class_idx, result.GetClass());
}
// Check if access is allowed. Unresolved types use xxxWithAccessCheck to
- // check at runtime if access is allowed and so pass here.
- if (!result.IsUnresolvedTypes() && !referrer.IsUnresolvedTypes() && !referrer.CanAccess(result)) {
+ // check at runtime if access is allowed and so pass here. If result is
+ // primitive, skip the access check.
+ if (result.IsNonZeroReferenceTypes() && !result.IsUnresolvedTypes() &&
+ !referrer.IsUnresolvedTypes() && !referrer.CanAccess(result)) {
Fail(VERIFY_ERROR_ACCESS_CLASS) << "illegal class access: '"
<< referrer << "' -> '" << result << "'";
}
@@ -3297,6 +3299,43 @@
}
}
+void MethodVerifier::VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
+ const uint32_t vregA) {
+ // Primitive assignability rules are weaker than regular assignability rules.
+ bool instruction_compatible;
+ bool value_compatible;
+ const RegType& value_type = work_line_->GetRegisterType(vregA);
+ if (target_type.IsIntegralTypes()) {
+ instruction_compatible = insn_type.IsIntegralTypes();
+ value_compatible = value_type.IsIntegralTypes();
+ } else if (target_type.IsFloat()) {
+ instruction_compatible = insn_type.IsInteger(); // no put-float, so expect put-int
+ value_compatible = value_type.IsFloatTypes();
+ } else if (target_type.IsLong()) {
+ instruction_compatible = insn_type.IsLong();
+ value_compatible = value_type.IsLongTypes();
+ } else if (target_type.IsDouble()) {
+ instruction_compatible = insn_type.IsLong(); // no put-double, so expect put-long
+ value_compatible = value_type.IsDoubleTypes();
+ } else {
+ instruction_compatible = false; // reference with primitive store
+ value_compatible = false; // unused
+ }
+ if (!instruction_compatible) {
+ // This is a global failure rather than a class change failure as the instructions and
+ // the descriptors for the type should have been consistent within the same file at
+ // compile time.
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "put insn has type '" << insn_type
+ << "' but expected type '" << target_type << "'";
+ return;
+ }
+ if (!value_compatible) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
+ << " of type " << value_type << " but expected " << target_type << " for put";
+ return;
+ }
+}
+
void MethodVerifier::VerifyAPut(const Instruction* inst,
const RegType& insn_type, bool is_primitive) {
const RegType& index_type = work_line_->GetRegisterType(inst->VRegC_23x());
@@ -3310,25 +3349,20 @@
} else if (!array_type.IsArrayTypes()) {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aput";
} else {
- /* verify the class */
const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_);
- if (!component_type.IsReferenceTypes() && !is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
- << " source for aput-object";
- } else if (component_type.IsNonZeroReferenceTypes() && is_primitive) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "reference array type " << array_type
- << " source for category 1 aput";
- } else if (is_primitive && !insn_type.Equals(component_type) &&
- !((insn_type.IsInteger() && component_type.IsFloat()) ||
- (insn_type.IsLong() && component_type.IsDouble()))) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
- << " incompatible with aput of type " << insn_type;
+ const uint32_t vregA = inst->VRegA_23x();
+ if (is_primitive) {
+ VerifyPrimitivePut(component_type, insn_type, vregA);
} else {
- // The instruction agrees with the type of array, confirm the value to be stored does too
- // Note: we use the instruction type (rather than the component type) for aput-object as
- // incompatible classes will be caught at runtime as an array store exception
- work_line_->VerifyRegisterType(inst->VRegA_23x(),
- is_primitive ? component_type : insn_type);
+ if (!component_type.IsReferenceTypes()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type
+ << " source for aput-object";
+ } else {
+ // The instruction agrees with the type of array, confirm the value to be stored does too
+ // Note: we use the instruction type (rather than the component type) for aput-object as
+ // incompatible classes will be caught at runtime as an array store exception
+ work_line_->VerifyRegisterType(vregA, insn_type);
+ }
}
}
}
@@ -3518,43 +3552,7 @@
}
const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c();
if (is_primitive) {
- // Primitive field assignability rules are weaker than regular assignability rules
- bool instruction_compatible;
- bool value_compatible;
- const RegType& value_type = work_line_->GetRegisterType(vregA);
- if (field_type.IsIntegralTypes()) {
- instruction_compatible = insn_type.IsIntegralTypes();
- value_compatible = value_type.IsIntegralTypes();
- } else if (field_type.IsFloat()) {
- instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int
- value_compatible = value_type.IsFloatTypes();
- } else if (field_type.IsLong()) {
- instruction_compatible = insn_type.IsLong();
- value_compatible = value_type.IsLongTypes();
- } else if (field_type.IsDouble()) {
- instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long
- value_compatible = value_type.IsDoubleTypes();
- } else {
- instruction_compatible = false; // reference field with primitive store
- value_compatible = false; // unused
- }
- if (!instruction_compatible) {
- // This is a global failure rather than a class change failure as the instructions and
- // the descriptors for the type should have been consistent within the same file at
- // compile time
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field)
- << " to be of type '" << insn_type
- << "' but found type '" << field_type
- << "' in put";
- return;
- }
- if (!value_compatible) {
- Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA
- << " of type " << value_type
- << " but expected " << field_type
- << " for store to " << PrettyField(field) << " in put";
- return;
- }
+ VerifyPrimitivePut(field_type, insn_type, vregA);
} else {
if (!insn_type.IsAssignableFrom(field_type)) {
Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field)
@@ -3756,6 +3754,10 @@
if (!insn_flags_[next_insn].IsReturn()) {
target_line->CopyFromLine(merge_line);
} else {
+ // Verify that the monitor stack is empty on return.
+ if (!merge_line->VerifyMonitorStackEmpty()) {
+ return false;
+ }
// For returns we only care about the operand to the return, all other registers are dead.
// Initialize them as conflicts so they don't add to GC and deoptimization information.
const Instruction* ret_inst = Instruction::At(code_item_->insns_ + next_insn);
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 3f98a00..c7b8a74 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -230,6 +230,10 @@
uint32_t access_flags, bool can_load_classes, bool allow_soft_failures)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ ~MethodVerifier() {
+ STLDeleteElements(&failure_messages_);
+ }
+
// Run verification on the method. Returns true if verification completes and false if the input
// has an irrecoverable corruption.
bool Verify() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -476,6 +480,10 @@
void VerifyNewArray(const Instruction* inst, bool is_filled, bool is_range)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ // Helper to perform verification on puts of primitive type.
+ void VerifyPrimitivePut(const RegType& target_type, const RegType& insn_type,
+ const uint32_t vregA) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
// Perform verification of an aget instruction. The destination register's type will be set to
// be that of component type of the array unless the array type is unknown, in which case a
// bottom type inferred from the type of instruction is used. is_primitive is false for an
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index 7965c06..24a626b 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -38,7 +38,7 @@
bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
DCHECK_LT(vdst, num_regs_);
if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
- verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Expected category1 register type not '"
+ verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
<< new_type << "'";
return false;
} else if (new_type.IsConflict()) { // should only be set during a merge
@@ -448,7 +448,7 @@
}
}
-bool RegisterLine::VerifyMonitorStackEmpty() {
+bool RegisterLine::VerifyMonitorStackEmpty() const {
if (MonitorStackDepth() != 0) {
verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected empty monitor stack";
return false;
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index f380877..f19dcca 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -268,7 +268,7 @@
// We expect no monitors to be held at certain points, such a method returns. Verify the stack
// is empty, failing and returning false if not.
- bool VerifyMonitorStackEmpty();
+ bool VerifyMonitorStackEmpty() const;
bool MergeRegisters(const RegisterLine* incoming_line)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);