1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef BERBERIS_ASSEMBLER_COMMON_H_
18 #define BERBERIS_ASSEMBLER_COMMON_H_
19
20 #include <stdint.h>
21
22 #include <string>
23 #include <utility>
24
25 #include "berberis/assembler/machine_code.h"
26 #include "berberis/base/arena_alloc.h"
27 #include "berberis/base/arena_vector.h"
28 #include "berberis/base/logging.h"
29 #include "berberis/base/macros.h" // DISALLOW_IMPLICIT_CONSTRUCTORS
30
31 namespace berberis {
32
33 class AssemblerBase {
34 public:
AssemblerBase(MachineCode * code)35 explicit AssemblerBase(MachineCode* code) : jumps_(code->arena()), code_(code) {}
36
~AssemblerBase()37 ~AssemblerBase() {}
38
39 class Label {
40 public:
Label()41 Label() : position_(kInvalid) {}
42
43 // Label position is offset from the start of MachineCode
44 // where it is bound to.
position()45 uint32_t position() const { return position_; }
46
Bind(uint32_t position)47 void Bind(uint32_t position) {
48 CHECK(!IsBound());
49 position_ = position;
50 }
51
IsBound()52 bool IsBound() const { return position_ != kInvalid; }
53
54 protected:
55 static const uint32_t kInvalid = 0xffffffff;
56 uint32_t position_;
57
58 private:
59 DISALLOW_COPY_AND_ASSIGN(Label);
60 };
61
pc()62 uint32_t pc() const { return code_->code_offset(); }
63
64 // GNU-assembler inspired names: https://sourceware.org/binutils/docs-2.42/as.html#g_t8byte
65 template <typename... Args>
Byte(Args...args)66 void Byte(Args... args) {
67 static_assert((std::is_same_v<Args, uint8_t> && ...));
68 (Emit8(args), ...);
69 }
70
71 template <typename... Args>
TwoByte(Args...args)72 void TwoByte(Args... args) {
73 static_assert((std::is_same_v<Args, uint16_t> && ...));
74 (Emit16(args), ...);
75 }
76
77 template <typename... Args>
FourByte(Args...args)78 void FourByte(Args... args) {
79 static_assert((std::is_same_v<Args, uint32_t> && ...));
80 (Emit32(args), ...);
81 }
82
83 template <typename... Args>
EigthByte(Args...args)84 void EigthByte(Args... args) {
85 static_assert((std::is_same_v<Args, uint64_t> && ...));
86 (Emit64(args), ...);
87 }
88
89 // Macro operations.
Emit8(uint8_t v)90 void Emit8(uint8_t v) { code_->AddU8(v); }
91
Emit16(int16_t v)92 void Emit16(int16_t v) { code_->Add<int16_t>(v); }
93
Emit32(int32_t v)94 void Emit32(int32_t v) { code_->Add<int32_t>(v); }
95
Emit64(int64_t v)96 void Emit64(int64_t v) { code_->Add<int64_t>(v); }
97
98 template <typename T>
EmitSequence(const T * v,uint32_t count)99 void EmitSequence(const T* v, uint32_t count) {
100 code_->AddSequence(v, sizeof(T) * count);
101 }
102
Bind(Label * label)103 void Bind(Label* label) { label->Bind(pc()); }
104
MakeLabel()105 Label* MakeLabel() { return NewInArena<Label>(code_->arena()); }
106
SetRecoveryPoint(Label * recovery_label)107 void SetRecoveryPoint(Label* recovery_label) {
108 jumps_.push_back(Jump{recovery_label, pc(), true});
109 }
110
111 protected:
112 template <typename T>
AddrAs(uint32_t offset)113 T* AddrAs(uint32_t offset) {
114 return code_->AddrAs<T>(offset);
115 }
116
AddRelocation(uint32_t dst,RelocationType type,uint32_t pc,intptr_t data)117 void AddRelocation(uint32_t dst, RelocationType type, uint32_t pc, intptr_t data) {
118 code_->AddRelocation(dst, type, pc, data);
119 }
120
121 // These are 'static' relocations, resolved when code is finalized.
122 // We also have 'dynamic' relocations, resolved when code is installed.
123 // TODO(b/232598137): rename Jump to something more appropriate since we are supporting
124 // memory-accessing instructions, not just jumps.
125 struct Jump {
126 const Label* label;
127 // Position of field to store offset. Note: unless it's recovery label precomputed
128 // "distance from the end of instruction" is stored there.
129 //
130 // This is needed because we keep pointer to the rip-offset field while value stored
131 // there is counted from the end of instruction (on x86) or, sometimes, from the end
132 // of next instruction (ARM).
133 uint32_t pc;
134 bool is_recovery;
135 };
136 using JumpList = ArenaVector<Jump>;
137 JumpList jumps_;
138
139 private:
140 MachineCode* code_;
141
142 DISALLOW_IMPLICIT_CONSTRUCTORS(AssemblerBase);
143 };
144
145 // Return the reverse condition. On all architectures that we may care about (AArch32/AArch64,
146 // RISC-V and x86) this can be achieved with a simple bitflop of the lowest bit.
147 // We may need a specialization of that function for more exotic architectures.
148 template <typename Condition>
ToReverseCond(Condition cond)149 inline constexpr Condition ToReverseCond(Condition cond) {
150 CHECK(cond != Condition::kInvalidCondition);
151 // Condition has a nice property that given a condition, you can get
152 // its reverse condition by flipping the least significant bit.
153 return Condition(static_cast<int>(cond) ^ 1);
154 }
155
156 } // namespace berberis
157
158 #endif // BERBERIS_ASSEMBLER_COMMON_H_
159