/*
* Copyright (C) 2015 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_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
#define ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_
#include <deque>
#include <vector>
#include "linker/relative_patcher.h"
#include "method_reference.h"
#include "safe_map.h"
namespace art {
namespace linker {
class ArmBaseRelativePatcher : public RelativePatcher {
public:
uint32_t ReserveSpace(uint32_t offset,
const CompiledMethod* compiled_method,
MethodReference method_ref) OVERRIDE;
uint32_t ReserveSpaceEnd(uint32_t offset) OVERRIDE;
uint32_t WriteThunks(OutputStream* out, uint32_t offset) OVERRIDE;
protected:
ArmBaseRelativePatcher(RelativePatcherTargetProvider* provider,
InstructionSet instruction_set);
~ArmBaseRelativePatcher();
enum class ThunkType {
kMethodCall, // Method call thunk.
kBakerReadBarrierField, // Baker read barrier, load field or array element at known offset.
kBakerReadBarrierRoot, // Baker read barrier, GC root load.
};
struct BakerReadBarrierOffsetParams {
uint32_t holder_reg; // Holder object for reading lock word.
uint32_t base_reg; // Base register, different from holder for large offset.
// If base differs from holder, it should be a pre-defined
// register to limit the number of thunks we need to emit.
// The offset is retrieved using introspection.
};
struct BakerReadBarrierRootParams {
uint32_t root_reg; // The register holding the GC root.
uint32_t dummy;
};
struct RawThunkParams {
uint32_t first;
uint32_t second;
};
union ThunkParams {
RawThunkParams raw_params;
BakerReadBarrierOffsetParams offset_params;
BakerReadBarrierRootParams root_params;
};
class ThunkKey {
public:
ThunkKey(ThunkType type, ThunkParams params) : type_(type), params_(params) { }
ThunkType GetType() const {
return type_;
}
BakerReadBarrierOffsetParams GetOffsetParams() const {
DCHECK(type_ == ThunkType::kBakerReadBarrierField);
return params_.offset_params;
}
BakerReadBarrierRootParams GetRootParams() const {
DCHECK(type_ == ThunkType::kBakerReadBarrierRoot);
return params_.root_params;
}
RawThunkParams GetRawParams() const {
return params_.raw_params;
}
private:
ThunkType type_;
ThunkParams params_;
};
class ThunkKeyCompare {
public:
bool operator()(const ThunkKey& lhs, const ThunkKey& rhs) const {
if (lhs.GetType() != rhs.GetType()) {
return lhs.GetType() < rhs.GetType();
}
if (lhs.GetRawParams().first != rhs.GetRawParams().first) {
return lhs.GetRawParams().first < rhs.GetRawParams().first;
}
return lhs.GetRawParams().second < rhs.GetRawParams().second;
}
};
uint32_t ReserveSpaceInternal(uint32_t offset,
const CompiledMethod* compiled_method,
MethodReference method_ref,
uint32_t max_extra_space);
uint32_t GetThunkTargetOffset(const ThunkKey& key, uint32_t patch_offset);
uint32_t CalculateMethodCallDisplacement(uint32_t patch_offset,
uint32_t target_offset);
virtual ThunkKey GetBakerReadBarrierKey(const LinkerPatch& patch) = 0;
virtual std::vector<uint8_t> CompileThunk(const ThunkKey& key) = 0;
virtual uint32_t MaxPositiveDisplacement(ThunkType type) = 0;
virtual uint32_t MaxNegativeDisplacement(ThunkType type) = 0;
private:
class ThunkData;
void ProcessPatches(const CompiledMethod* compiled_method, uint32_t code_offset);
void AddUnreservedThunk(ThunkData* data);
void ResolveMethodCalls(uint32_t quick_code_offset, MethodReference method_ref);
uint32_t CalculateMaxNextOffset(uint32_t patch_offset, ThunkType type);
RelativePatcherTargetProvider* const provider_;
const InstructionSet instruction_set_;
// The data for all thunks.
// SafeMap<> nodes don't move after being inserted, so we can use direct pointers to the data.
using ThunkMap = SafeMap<ThunkKey, ThunkData, ThunkKeyCompare>;
ThunkMap thunks_;
// ReserveSpace() tracks unprocessed method call patches. These may be resolved later.
class UnprocessedMethodCallPatch {
public:
UnprocessedMethodCallPatch(uint32_t patch_offset, MethodReference target_method)
: patch_offset_(patch_offset), target_method_(target_method) { }
uint32_t GetPatchOffset() const {
return patch_offset_;
}
MethodReference GetTargetMethod() const {
return target_method_;
}
private:
uint32_t patch_offset_;
MethodReference target_method_;
};
std::deque<UnprocessedMethodCallPatch> unprocessed_method_call_patches_;
// Once we have compiled a method call thunk, cache pointer to the data.
ThunkData* method_call_thunk_;
// Thunks
std::deque<ThunkData*> unreserved_thunks_;
class PendingThunkComparator;
std::vector<ThunkData*> pending_thunks_; // Heap with the PendingThunkComparator.
friend class Arm64RelativePatcherTest;
friend class Thumb2RelativePatcherTest;
DISALLOW_COPY_AND_ASSIGN(ArmBaseRelativePatcher);
};
} // namespace linker
} // namespace art
#endif // ART_COMPILER_LINKER_ARM_RELATIVE_PATCHER_ARM_BASE_H_