Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(711)

Side by Side Diff: src/IceInstARM32.h

Issue 1127963004: Subzero ARM: lowerArguments (GPR), basic legalize(), and lowerRet(i32, i64). (Closed) Base URL: https://chromium.googlesource.com/native_client/pnacl-subzero.git@master
Patch Set: fix warnings, etc Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/IceInst.cpp ('k') | src/IceInstARM32.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 //===- subzero/src/IceInstARM32.h - ARM32 machine instructions --*- C++ -*-===// 1 //===- subzero/src/IceInstARM32.h - ARM32 machine instructions --*- C++ -*-===//
2 // 2 //
3 // The Subzero Code Generator 3 // The Subzero Code Generator
4 // 4 //
5 // This file is distributed under the University of Illinois Open Source 5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details. 6 // License. See LICENSE.TXT for details.
7 // 7 //
8 //===----------------------------------------------------------------------===// 8 //===----------------------------------------------------------------------===//
9 // 9 //
10 // This file declares the InstARM32 and OperandARM32 classes and 10 // This file declares the InstARM32 and OperandARM32 classes and
11 // their subclasses. This represents the machine instructions and 11 // their subclasses. This represents the machine instructions and
12 // operands used for ARM32 code selection. 12 // operands used for ARM32 code selection.
13 // 13 //
14 //===----------------------------------------------------------------------===// 14 //===----------------------------------------------------------------------===//
15 15
16 #ifndef SUBZERO_SRC_ICEINSTARM32_H 16 #ifndef SUBZERO_SRC_ICEINSTARM32_H
17 #define SUBZERO_SRC_ICEINSTARM32_H 17 #define SUBZERO_SRC_ICEINSTARM32_H
18 18
19 #include "IceDefs.h" 19 #include "IceDefs.h"
20 #include "IceInst.h" 20 #include "IceInst.h"
21 #include "IceInstARM32.def" 21 #include "IceInstARM32.def"
22 #include "IceOperand.h" 22 #include "IceOperand.h"
23 23
24 namespace Ice { 24 namespace Ice {
25 25
26 class TargetARM32; 26 class TargetARM32;
27 27
28 // OperandARM32 extends the Operand hierarchy. 28 // OperandARM32 extends the Operand hierarchy. Its subclasses are
29 // TODO(jvoung): Add the OperandARM32Mem and OperandARM32Flex. 29 // OperandARM32Mem and OperandARM32Flex.
30 class OperandARM32 : public Operand { 30 class OperandARM32 : public Operand {
31 OperandARM32() = delete; 31 OperandARM32() = delete;
32 OperandARM32(const OperandARM32 &) = delete; 32 OperandARM32(const OperandARM32 &) = delete;
33 OperandARM32 &operator=(const OperandARM32 &) = delete; 33 OperandARM32 &operator=(const OperandARM32 &) = delete;
34 34
35 public: 35 public:
36 enum OperandKindARM32 { k__Start = Operand::kTarget }; 36 enum OperandKindARM32 {
37 k__Start = Operand::kTarget,
38 kMem,
39 kFlexStart,
40 kFlexImm = kFlexStart,
41 kFlexReg,
42 kFlexEnd = kFlexReg
43 };
37 44
38 enum ShiftKind { 45 enum ShiftKind {
39 kNoShift = -1, 46 kNoShift = -1,
40 #define X(enum, emit) enum, 47 #define X(enum, emit) enum,
41 ICEINSTARM32SHIFT_TABLE 48 ICEINSTARM32SHIFT_TABLE
42 #undef X 49 #undef X
43 }; 50 };
44 51
45 using Operand::dump; 52 using Operand::dump;
46 void dump(const Cfg *, Ostream &Str) const override { 53 void dump(const Cfg *, Ostream &Str) const override {
47 if (ALLOW_DUMP) 54 if (ALLOW_DUMP)
48 Str << "<OperandARM32>"; 55 Str << "<OperandARM32>";
49 } 56 }
50 57
51 protected: 58 protected:
52 OperandARM32(OperandKindARM32 Kind, Type Ty) 59 OperandARM32(OperandKindARM32 Kind, Type Ty)
53 : Operand(static_cast<OperandKind>(Kind), Ty) {} 60 : Operand(static_cast<OperandKind>(Kind), Ty) {}
54 ~OperandARM32() override {} 61 ~OperandARM32() override {}
55 }; 62 };
56 63
57 // OperandARM32Mem represents a memory operand in any of the various ARM32 64 // OperandARM32Mem represents a memory operand in any of the various ARM32
58 // addressing modes. 65 // addressing modes.
59 // TODO(jvoung): Fill out more.
60 class OperandARM32Mem : public OperandARM32 { 66 class OperandARM32Mem : public OperandARM32 {
61 OperandARM32Mem() = delete; 67 OperandARM32Mem() = delete;
62 OperandARM32Mem(const OperandARM32Mem &) = delete; 68 OperandARM32Mem(const OperandARM32Mem &) = delete;
63 OperandARM32Mem &operator=(const OperandARM32Mem &) = delete; 69 OperandARM32Mem &operator=(const OperandARM32Mem &) = delete;
64 70
65 public: 71 public:
72 // Memory operand addressing mode.
73 // The enum value also carries the encoding.
74 // TODO(jvoung): unify with the assembler.
75 enum AddrMode {
76 // bit encoding P U W
77 Offset = (8 | 4 | 0) << 21, // offset (w/o writeback to base)
78 PreIndex = (8 | 4 | 1) << 21, // pre-indexed addressing with writeback
79 PostIndex = (0 | 4 | 0) << 21, // post-indexed addressing with writeback
80 NegOffset = (8 | 0 | 0) << 21, // negative offset (w/o writeback to base)
81 NegPreIndex = (8 | 0 | 1) << 21, // negative pre-indexed with writeback
82 NegPostIndex = (0 | 0 | 0) << 21 // negative post-indexed with writeback
83 };
84
85 // Provide two constructors.
86 // NOTE: The Variable-typed operands have to be registers.
87 //
88 // (1) Reg + Imm. The Immediate actually has a limited number of bits
89 // for encoding, so check canHoldOffset first. It cannot handle
90 // general Constant operands like ConstantRelocatable, since a relocatable
91 // can potentially take up too many bits.
92 static OperandARM32Mem *create(Cfg *Func, Type Ty, Variable *Base,
93 ConstantInteger32 *ImmOffset = nullptr,
94 AddrMode Mode = Offset) {
95 return new (Func->allocate<OperandARM32Mem>())
96 OperandARM32Mem(Func, Ty, Base, ImmOffset, Mode);
97 }
98 // (2) Reg +/- Reg with an optional shift of some kind and amount.
99 // Note that this mode is disallowed in the NaCl sandbox.
100 static OperandARM32Mem *create(Cfg *Func, Type Ty, Variable *Base,
101 Variable *Index, ShiftKind ShiftOp = kNoShift,
102 uint16_t ShiftAmt = 0,
103 AddrMode Mode = Offset) {
104 return new (Func->allocate<OperandARM32Mem>())
105 OperandARM32Mem(Func, Ty, Base, Index, ShiftOp, ShiftAmt, Mode);
106 }
107 Variable *getBase() const { return Base; }
108 ConstantInteger32 *getOffset() const { return ImmOffset; }
109 Variable *getIndex() const { return Index; }
110 ShiftKind getShiftOp() const { return ShiftOp; }
111 uint16_t getShiftAmt() const { return ShiftAmt; }
112 AddrMode getAddrMode() const { return Mode; }
113
114 bool isRegReg() const { return Index != nullptr; }
115 bool isNegAddrMode() const { return Mode >= NegOffset; }
116
117 void emit(const Cfg *Func) const override;
118 using OperandARM32::dump;
119 void dump(const Cfg *Func, Ostream &Str) const override;
120
121 static bool classof(const Operand *Operand) {
122 return Operand->getKind() == static_cast<OperandKind>(kMem);
123 }
124
66 // Return true if a load/store instruction for an element of type Ty 125 // Return true if a load/store instruction for an element of type Ty
67 // can encode the Offset directly in the immediate field of the 32-bit 126 // can encode the Offset directly in the immediate field of the 32-bit
68 // ARM instruction. For some types, if the load is Sign extending, then 127 // ARM instruction. For some types, if the load is Sign extending, then
69 // the range is reduced. 128 // the range is reduced.
70 static bool canHoldOffset(Type Ty, bool SignExt, int32_t Offset); 129 static bool canHoldOffset(Type Ty, bool SignExt, int32_t Offset);
130
131 private:
132 OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base,
133 ConstantInteger32 *ImmOffset, AddrMode Mode);
134 OperandARM32Mem(Cfg *Func, Type Ty, Variable *Base, Variable *Index,
135 ShiftKind ShiftOp, uint16_t ShiftAmt, AddrMode Mode);
136 ~OperandARM32Mem() override {}
137 Variable *Base;
138 ConstantInteger32 *ImmOffset;
139 Variable *Index;
140 ShiftKind ShiftOp;
141 uint16_t ShiftAmt;
142 AddrMode Mode;
143 };
144
145 // OperandARM32Flex represent the "flexible second operand" for
146 // data-processing instructions. It can be a rotatable 8-bit constant, or
147 // a register with an optional shift operand. The shift amount can even be
148 // a third register.
149 class OperandARM32Flex : public OperandARM32 {
150 OperandARM32Flex() = delete;
151 OperandARM32Flex(const OperandARM32Flex &) = delete;
152 OperandARM32Flex &operator=(const OperandARM32Flex &) = delete;
153
154 public:
155 static bool classof(const Operand *Operand) {
156 return static_cast<OperandKind>(kFlexStart) <= Operand->getKind() &&
157 Operand->getKind() <= static_cast<OperandKind>(kFlexEnd);
158 }
159
160 protected:
161 OperandARM32Flex(OperandKindARM32 Kind, Type Ty) : OperandARM32(Kind, Ty) {}
162 ~OperandARM32Flex() override {}
163 };
164
165 // Rotated immediate variant.
166 class OperandARM32FlexImm : public OperandARM32Flex {
167 OperandARM32FlexImm() = delete;
168 OperandARM32FlexImm(const OperandARM32FlexImm &) = delete;
169 OperandARM32FlexImm &operator=(const OperandARM32FlexImm &) = delete;
170
171 public:
172 // Immed_8 rotated by an even number of bits (2 * RotateAmt).
173 static OperandARM32FlexImm *create(Cfg *Func, Type Ty, uint32_t Imm,
174 uint32_t RotateAmt) {
175 return new (Func->allocate<OperandARM32FlexImm>())
176 OperandARM32FlexImm(Func, Ty, Imm, RotateAmt);
177 }
178
179 void emit(const Cfg *Func) const override;
180 using OperandARM32::dump;
181 void dump(const Cfg *Func, Ostream &Str) const override;
182
183 static bool classof(const Operand *Operand) {
184 return Operand->getKind() == static_cast<OperandKind>(kFlexImm);
185 }
186
187 // Return true if the Immediate can fit in the ARM flexible operand.
188 // Fills in the out-params RotateAmt and Immed_8 if Immediate fits.
189 static bool canHoldImm(uint32_t Immediate, uint32_t *RotateAmt,
190 uint32_t *Immed_8);
191
192 uint32_t getImm() const { return Imm; }
193 uint32_t getRotateAmt() const { return RotateAmt; }
194
195 private:
196 OperandARM32FlexImm(Cfg *Func, Type Ty, uint32_t Imm, uint32_t RotateAmt);
197 ~OperandARM32FlexImm() override {}
198
199 uint32_t Imm;
200 uint32_t RotateAmt;
201 };
202
203 // Shifted register variant.
204 class OperandARM32FlexReg : public OperandARM32Flex {
205 OperandARM32FlexReg() = delete;
206 OperandARM32FlexReg(const OperandARM32FlexReg &) = delete;
207 OperandARM32FlexReg &operator=(const OperandARM32FlexReg &) = delete;
208
209 public:
210 // Register with immediate/reg shift amount and shift operation.
211 static OperandARM32FlexReg *create(Cfg *Func, Type Ty, Variable *Reg,
212 ShiftKind ShiftOp, Operand *ShiftAmt) {
213 return new (Func->allocate<OperandARM32FlexReg>())
214 OperandARM32FlexReg(Func, Ty, Reg, ShiftOp, ShiftAmt);
215 }
216
217 void emit(const Cfg *Func) const override;
218 using OperandARM32::dump;
219 void dump(const Cfg *Func, Ostream &Str) const override;
220
221 static bool classof(const Operand *Operand) {
222 return Operand->getKind() == static_cast<OperandKind>(kFlexReg);
223 }
224
225 Variable *getReg() const { return Reg; }
226 ShiftKind getShiftOp() const { return ShiftOp; }
227 // ShiftAmt can represent an immediate or a register.
228 Operand *getShiftAmt() const { return ShiftAmt; }
229
230 private:
231 OperandARM32FlexReg(Cfg *Func, Type Ty, Variable *Reg, ShiftKind ShiftOp,
232 Operand *ShiftAmt);
233 ~OperandARM32FlexReg() override {}
234
235 Variable *Reg;
236 ShiftKind ShiftOp;
237 Operand *ShiftAmt;
71 }; 238 };
72 239
73 class InstARM32 : public InstTarget { 240 class InstARM32 : public InstTarget {
74 InstARM32() = delete; 241 InstARM32() = delete;
75 InstARM32(const InstARM32 &) = delete; 242 InstARM32(const InstARM32 &) = delete;
76 InstARM32 &operator=(const InstARM32 &) = delete; 243 InstARM32 &operator=(const InstARM32 &) = delete;
77 244
78 public: 245 public:
79 enum InstKindARM32 { k__Start = Inst::Target, Ret }; 246 enum InstKindARM32 {
247 k__Start = Inst::Target,
248 Mov,
249 Movt,
250 Movw,
251 Mvn,
252 Ret,
253 Ldr
254 };
80 255
81 static const char *getWidthString(Type Ty); 256 static const char *getWidthString(Type Ty);
82 257
83 void dump(const Cfg *Func) const override; 258 void dump(const Cfg *Func) const override;
84 259
85 protected: 260 protected:
86 InstARM32(Cfg *Func, InstKindARM32 Kind, SizeT Maxsrcs, Variable *Dest) 261 InstARM32(Cfg *Func, InstKindARM32 Kind, SizeT Maxsrcs, Variable *Dest)
87 : InstTarget(Func, static_cast<InstKind>(Kind), Maxsrcs, Dest) {} 262 : InstTarget(Func, static_cast<InstKind>(Kind), Maxsrcs, Dest) {}
88 ~InstARM32() override {} 263 ~InstARM32() override {}
89 static bool isClassof(const Inst *Inst, InstKindARM32 MyKind) { 264 static bool isClassof(const Inst *Inst, InstKindARM32 MyKind) {
90 return Inst->getKind() == static_cast<InstKind>(MyKind); 265 return Inst->getKind() == static_cast<InstKind>(MyKind);
91 } 266 }
92 }; 267 };
93 268
269 void emitTwoAddr(const char *Opcode, const Inst *Inst, const Cfg *Func);
270
271 // TODO(jvoung): add condition codes if instruction can be predicated.
272
273 // Instructions of the form x := op(y).
274 template <InstARM32::InstKindARM32 K>
275 class InstARM32UnaryopGPR : public InstARM32 {
276 InstARM32UnaryopGPR() = delete;
277 InstARM32UnaryopGPR(const InstARM32UnaryopGPR &) = delete;
278 InstARM32UnaryopGPR &operator=(const InstARM32UnaryopGPR &) = delete;
279
280 public:
281 static InstARM32UnaryopGPR *create(Cfg *Func, Variable *Dest, Operand *Src) {
282 return new (Func->allocate<InstARM32UnaryopGPR>())
283 InstARM32UnaryopGPR(Func, Dest, Src);
284 }
285 void emit(const Cfg *Func) const override {
286 if (!ALLOW_DUMP)
287 return;
288 Ostream &Str = Func->getContext()->getStrEmit();
289 assert(getSrcSize() == 1);
290 Str << "\t" << Opcode << "\t";
291 getDest()->emit(Func);
292 Str << ", ";
293 getSrc(0)->emit(Func);
294 }
295 void emitIAS(const Cfg *Func) const override {
296 (void)Func;
297 llvm_unreachable("Not yet implemented");
298 }
299 void dump(const Cfg *Func) const override {
300 if (!ALLOW_DUMP)
301 return;
302 Ostream &Str = Func->getContext()->getStrDump();
303 dumpDest(Func);
304 Str << " = " << Opcode << "." << getDest()->getType() << " ";
305 dumpSources(Func);
306 }
307 static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
308
309 private:
310 InstARM32UnaryopGPR(Cfg *Func, Variable *Dest, Operand *Src)
311 : InstARM32(Func, K, 1, Dest) {
312 addSource(Src);
313 }
314 ~InstARM32UnaryopGPR() override {}
315 static const char *Opcode;
316 };
317
318 // Instructions of the form x := x op y.
319 template <InstARM32::InstKindARM32 K>
320 class InstARM32TwoAddrGPR : public InstARM32 {
321 InstARM32TwoAddrGPR() = delete;
322 InstARM32TwoAddrGPR(const InstARM32TwoAddrGPR &) = delete;
323 InstARM32TwoAddrGPR &operator=(const InstARM32TwoAddrGPR &) = delete;
324
325 public:
326 // Dest must be a register.
327 static InstARM32TwoAddrGPR *create(Cfg *Func, Variable *Dest, Operand *Src) {
328 return new (Func->allocate<InstARM32TwoAddrGPR>())
329 InstARM32TwoAddrGPR(Func, Dest, Src);
330 }
331 void emit(const Cfg *Func) const override {
332 if (!ALLOW_DUMP)
333 return;
334 emitTwoAddr(Opcode, this, Func);
335 }
336 void emitIAS(const Cfg *Func) const override {
337 (void)Func;
338 llvm::report_fatal_error("Not yet implemented");
339 }
340 void dump(const Cfg *Func) const override {
341 if (!ALLOW_DUMP)
342 return;
343 Ostream &Str = Func->getContext()->getStrDump();
344 dumpDest(Func);
345 Str << " = " << Opcode << "." << getDest()->getType() << " ";
346 dumpSources(Func);
347 }
348 static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
349
350 private:
351 InstARM32TwoAddrGPR(Cfg *Func, Variable *Dest, Operand *Src)
352 : InstARM32(Func, K, 2, Dest) {
353 addSource(Dest);
354 addSource(Src);
355 }
356 ~InstARM32TwoAddrGPR() override {}
357 static const char *Opcode;
358 };
359
360 // Base class for assignment instructions.
361 // These can be tested for redundancy (and elided if redundant).
362 template <InstARM32::InstKindARM32 K>
363 class InstARM32Movlike : public InstARM32 {
364 InstARM32Movlike() = delete;
365 InstARM32Movlike(const InstARM32Movlike &) = delete;
366 InstARM32Movlike &operator=(const InstARM32Movlike &) = delete;
367
368 public:
369 static InstARM32Movlike *create(Cfg *Func, Variable *Dest, Operand *Source) {
370 return new (Func->allocate<InstARM32Movlike>())
371 InstARM32Movlike(Func, Dest, Source);
372 }
373 bool isRedundantAssign() const override {
374 return checkForRedundantAssign(getDest(), getSrc(0));
375 }
376 bool isSimpleAssign() const override { return true; }
377 void emit(const Cfg *Func) const override;
378 void emitIAS(const Cfg *Func) const override;
379 void dump(const Cfg *Func) const override {
380 if (!ALLOW_DUMP)
381 return;
382 Ostream &Str = Func->getContext()->getStrDump();
383 Str << Opcode << "." << getDest()->getType() << " ";
384 dumpDest(Func);
385 Str << ", ";
386 dumpSources(Func);
387 }
388 static bool classof(const Inst *Inst) { return isClassof(Inst, K); }
389
390 private:
391 InstARM32Movlike(Cfg *Func, Variable *Dest, Operand *Source)
392 : InstARM32(Func, K, 1, Dest) {
393 addSource(Source);
394 }
395 ~InstARM32Movlike() override {}
396
397 static const char *Opcode;
398 };
399
400 // Move instruction (variable <- flex). This is more of a pseudo-inst.
401 // If var is a register, then we use "mov". If var is stack, then we use
402 // "str" to store to the stack.
403 typedef InstARM32Movlike<InstARM32::Mov> InstARM32Mov;
404 // MovT leaves the bottom bits alone so dest is also a source.
405 // This helps indicate that a previous MovW setting dest is not dead code.
406 typedef InstARM32TwoAddrGPR<InstARM32::Movt> InstARM32Movt;
407 typedef InstARM32UnaryopGPR<InstARM32::Movw> InstARM32Movw;
408 typedef InstARM32UnaryopGPR<InstARM32::Mvn> InstARM32Mvn;
409
410 // Load instruction.
411 class InstARM32Ldr : public InstARM32 {
412 InstARM32Ldr() = delete;
413 InstARM32Ldr(const InstARM32Ldr &) = delete;
414 InstARM32Ldr &operator=(const InstARM32Ldr &) = delete;
415
416 public:
417 // Dest must be a register.
418 static InstARM32Ldr *create(Cfg *Func, Variable *Dest, OperandARM32Mem *Mem) {
419 return new (Func->allocate<InstARM32Ldr>()) InstARM32Ldr(Func, Dest, Mem);
420 }
421 void emit(const Cfg *Func) const override;
422 void emitIAS(const Cfg *Func) const override;
423 void dump(const Cfg *Func) const override;
424 static bool classof(const Inst *Inst) { return isClassof(Inst, Ldr); }
425
426 private:
427 InstARM32Ldr(Cfg *Func, Variable *Dest, OperandARM32Mem *Mem);
428 ~InstARM32Ldr() override {}
429 };
430
94 // Ret pseudo-instruction. This is actually a "bx" instruction with 431 // Ret pseudo-instruction. This is actually a "bx" instruction with
95 // an "lr" register operand, but epilogue lowering will search for a Ret 432 // an "lr" register operand, but epilogue lowering will search for a Ret
96 // instead of a generic "bx". This instruction also takes a Source 433 // instead of a generic "bx". This instruction also takes a Source
97 // operand (for non-void returning functions) for liveness analysis, though 434 // operand (for non-void returning functions) for liveness analysis, though
98 // a FakeUse before the ret would do just as well. 435 // a FakeUse before the ret would do just as well.
99 class InstARM32Ret : public InstARM32 { 436 class InstARM32Ret : public InstARM32 {
100 InstARM32Ret() = delete; 437 InstARM32Ret() = delete;
101 InstARM32Ret(const InstARM32Ret &) = delete; 438 InstARM32Ret(const InstARM32Ret &) = delete;
102 InstARM32Ret &operator=(const InstARM32Ret &) = delete; 439 InstARM32Ret &operator=(const InstARM32Ret &) = delete;
103 440
104 public: 441 public:
105 static InstARM32Ret *create(Cfg *Func, Variable *LR, 442 static InstARM32Ret *create(Cfg *Func, Variable *LR,
106 Variable *Source = nullptr) { 443 Variable *Source = nullptr) {
107 return new (Func->allocate<InstARM32Ret>()) InstARM32Ret(Func, LR, Source); 444 return new (Func->allocate<InstARM32Ret>()) InstARM32Ret(Func, LR, Source);
108 } 445 }
109 void emit(const Cfg *Func) const override; 446 void emit(const Cfg *Func) const override;
110 void emitIAS(const Cfg *Func) const override; 447 void emitIAS(const Cfg *Func) const override;
111 void dump(const Cfg *Func) const override; 448 void dump(const Cfg *Func) const override;
112 static bool classof(const Inst *Inst) { return isClassof(Inst, Ret); } 449 static bool classof(const Inst *Inst) { return isClassof(Inst, Ret); }
113 450
114 private: 451 private:
115 InstARM32Ret(Cfg *Func, Variable *LR, Variable *Source); 452 InstARM32Ret(Cfg *Func, Variable *LR, Variable *Source);
116 ~InstARM32Ret() override {} 453 ~InstARM32Ret() override {}
117 }; 454 };
118 455
456 // Declare partial template specializations of emit() methods that
457 // already have default implementations. Without this, there is the
458 // possibility of ODR violations and link errors.
459
460 template <> void InstARM32Movw::emit(const Cfg *Func) const;
461 template <> void InstARM32Movt::emit(const Cfg *Func) const;
462
119 } // end of namespace Ice 463 } // end of namespace Ice
120 464
121 #endif // SUBZERO_SRC_ICEINSTARM32_H 465 #endif // SUBZERO_SRC_ICEINSTARM32_H
OLDNEW
« no previous file with comments | « src/IceInst.cpp ('k') | src/IceInstARM32.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698