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