c07a81606841a45a474a271dd26a85fda1673453
[oota-llvm.git] / lib / Target / ARM / ARMAsmBackend.cpp
1 //===-- ARMAsmBackend.cpp - ARM Assembler Backend -------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "llvm/Target/TargetAsmBackend.h"
11 #include "ARM.h"
12 #include "ARMFixupKinds.h"
13 #include "llvm/ADT/Twine.h"
14 #include "llvm/MC/MCAssembler.h"
15 #include "llvm/MC/MCExpr.h"
16 #include "llvm/MC/MCObjectFormat.h"
17 #include "llvm/MC/MCObjectWriter.h"
18 #include "llvm/MC/MCSectionELF.h"
19 #include "llvm/MC/MCSectionMachO.h"
20 #include "llvm/Object/MachOFormat.h"
21 #include "llvm/Support/ELF.h"
22 #include "llvm/Support/ErrorHandling.h"
23 #include "llvm/Support/raw_ostream.h"
24 #include "llvm/Target/TargetRegistry.h"
25 using namespace llvm;
26
27 namespace {
28 class ARMAsmBackend : public TargetAsmBackend {
29 public:
30   ARMAsmBackend(const Target &T) : TargetAsmBackend() {}
31
32   bool MayNeedRelaxation(const MCInst &Inst) const;
33
34   void RelaxInstruction(const MCInst &Inst, MCInst &Res) const;
35
36   bool WriteNopData(uint64_t Count, MCObjectWriter *OW) const;
37
38   unsigned getPointerSize() const {
39     return 4;
40   }
41 };
42 } // end anonymous namespace
43
44 bool ARMAsmBackend::MayNeedRelaxation(const MCInst &Inst) const {
45   // FIXME: Thumb targets, different move constant targets..
46   return false;
47 }
48
49 void ARMAsmBackend::RelaxInstruction(const MCInst &Inst, MCInst &Res) const {
50   assert(0 && "ARMAsmBackend::RelaxInstruction() unimplemented");
51   return;
52 }
53
54 bool ARMAsmBackend::WriteNopData(uint64_t Count, MCObjectWriter *OW) const {
55   // FIXME: Zero fill for now. That's not right, but at least will get the
56   // section size right.
57   for (uint64_t i = 0; i != Count; ++i)
58     OW->Write8(0);
59   return true;
60 }
61
62 static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) {
63   switch (Kind) {
64   default:
65     llvm_unreachable("Unknown fixup kind!");
66   case FK_Data_4:
67   case ARM::fixup_arm_movt_hi16:
68   case ARM::fixup_arm_movw_lo16:
69     return Value;
70   case ARM::fixup_arm_pcrel_12: {
71     bool isAdd = true;
72     // ARM PC-relative values are offset by 8.
73     Value -= 8;
74     if ((int64_t)Value < 0) {
75       Value = -Value;
76       isAdd = false;
77     }
78     assert ((Value < 4096) && "Out of range pc-relative fixup value!");
79     Value |= isAdd << 23;
80     return Value;
81   }
82   case ARM::fixup_arm_branch:
83     // These values don't encode the low two bits since they're always zero.
84     // Offset by 8 just as above.
85     return (Value - 8) >> 2;
86   case ARM::fixup_arm_pcrel_10: {
87     // Offset by 8 just as above.
88     Value = Value - 8;
89     bool isAdd = true;
90     if ((int64_t)Value < 0) {
91       Value = -Value;
92       isAdd = false;
93     }
94     // These values don't encode the low two bits since they're always zero.
95     Value >>= 2;
96     assert ((Value < 256) && "Out of range pc-relative fixup value!");
97     Value |= isAdd << 23;
98     return Value;
99   }
100   }
101 }
102
103 namespace {
104 // FIXME: This should be in a separate file.
105 // ELF is an ELF of course...
106 class ELFARMAsmBackend : public ARMAsmBackend {
107   MCELFObjectFormat Format;
108
109 public:
110   Triple::OSType OSType;
111   ELFARMAsmBackend(const Target &T, Triple::OSType _OSType)
112     : ARMAsmBackend(T), OSType(_OSType) {
113     HasScatteredSymbols = true;
114   }
115
116   virtual const MCObjectFormat &getObjectFormat() const {
117     return Format;
118   }
119
120   void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
121                   uint64_t Value) const;
122
123   MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
124     return createELFObjectWriter(OS, /*Is64Bit=*/false,
125                                  OSType, ELF::EM_ARM,
126                                  /*IsLittleEndian=*/true,
127                                  /*HasRelocationAddend=*/false);
128   }
129 };
130
131 // Fixme: can we raise this to share code between Darwin and ELF?
132 void ELFARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
133                                   uint64_t Value) const {
134   uint32_t Mask = 0;
135   // Fixme: 2 for Thumb
136   unsigned NumBytes = 4;
137   Value = adjustFixupValue(Fixup.getKind(), Value);
138
139   switch (Fixup.getKind()) {
140   default: assert(0 && "Unsupported Fixup kind"); break;
141   case ARM::fixup_arm_branch: {
142     unsigned Lo24 = Value & 0xFFFFFF;
143     Mask = ~(0xFFFFFF);
144     Value = Lo24;
145   }; break;
146   case ARM::fixup_arm_movt_hi16:
147   case ARM::fixup_arm_movw_lo16: {
148     unsigned Hi4 = (Value & 0xF000) >> 12;
149     unsigned Lo12 = Value & 0x0FFF;
150     // inst{19-16} = Hi4;
151     // inst{11-0} = Lo12;
152     Value = (Hi4 << 16) | (Lo12);
153     Mask = ~(0xF0FFF);
154   }; break;
155   }
156
157   assert((Fixup.getOffset() % NumBytes == 0)
158          && "Offset mod NumBytes is nonzero!");
159   // For each byte of the fragment that the fixup touches, mask in the
160   // bits from the fixup value.
161   // The Value has been "split up" into the appropriate bitfields above.
162   // Fixme: how to share code with the .td generated code?
163   for (unsigned i = 0; i != NumBytes; ++i) {
164     DF.getContents()[Fixup.getOffset() + i] &= uint8_t(Mask >> (i * 8));
165     DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8));
166   }
167 }
168
169 namespace {
170 // FIXME: This should be in a separate file.
171 class DarwinARMAsmBackend : public ARMAsmBackend {
172   MCMachOObjectFormat Format;
173 public:
174   DarwinARMAsmBackend(const Target &T) : ARMAsmBackend(T) {
175     HasScatteredSymbols = true;
176   }
177
178   virtual const MCObjectFormat &getObjectFormat() const {
179     return Format;
180   }
181
182   void ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
183                   uint64_t Value) const;
184
185   MCObjectWriter *createObjectWriter(raw_ostream &OS) const {
186     // FIXME: Subtarget info should be derived. Force v7 for now.
187     return createMachObjectWriter(OS, /*Is64Bit=*/false,
188                                   object::mach::CTM_ARM,
189                                   object::mach::CSARM_V7,
190                                   /*IsLittleEndian=*/true);
191   }
192
193   virtual bool doesSectionRequireSymbols(const MCSection &Section) const {
194     return false;
195   }
196 };
197 } // end anonymous namespace
198
199 static unsigned getFixupKindNumBytes(unsigned Kind) {
200   switch (Kind) {
201   default: llvm_unreachable("Unknown fixup kind!");
202   case FK_Data_4: return 4;
203   case ARM::fixup_arm_pcrel_12: return 3;
204   case ARM::fixup_arm_pcrel_10: return 3;
205   case ARM::fixup_arm_branch: return 3;
206   }
207 }
208
209 void DarwinARMAsmBackend::ApplyFixup(const MCFixup &Fixup, MCDataFragment &DF,
210                                      uint64_t Value) const {
211   unsigned NumBytes = getFixupKindNumBytes(Fixup.getKind());
212   Value = adjustFixupValue(Fixup.getKind(), Value);
213
214   assert(Fixup.getOffset() + NumBytes <= DF.getContents().size() &&
215          "Invalid fixup offset!");
216   // For each byte of the fragment that the fixup touches, mask in the
217   // bits from the fixup value.
218   for (unsigned i = 0; i != NumBytes; ++i)
219     DF.getContents()[Fixup.getOffset() + i] |= uint8_t(Value >> (i * 8));
220 }
221 } // end anonymous namespace
222
223 TargetAsmBackend *llvm::createARMAsmBackend(const Target &T,
224                                             const std::string &TT) {
225   switch (Triple(TT).getOS()) {
226   case Triple::Darwin:
227     return new DarwinARMAsmBackend(T);
228   case Triple::MinGW32:
229   case Triple::Cygwin:
230   case Triple::Win32:
231     assert(0 && "Windows not supported on ARM");
232   default:
233     return new ELFARMAsmBackend(T, Triple(TT).getOS());
234   }
235 }